(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o ./dist/ccxt.browser.js */ window.ccxt = require ('./ccxt') },{"./ccxt":2}],2:[function(require,module,exports){ "use strict"; /* MIT License Copyright (c) 2017 Igor Kroitor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //----------------------------------------------------------------------------- const Exchange = require ('./js/base/Exchange') , functions = require ('./js/base/functions') , errors = require ('./js/base/errors') //----------------------------------------------------------------------------- // this is updated by vss.js when building const version = '1.27.45' Exchange.ccxtVersion = version //----------------------------------------------------------------------------- const exchanges = { '_1btcxe': require ('./js/_1btcxe.js'), 'acx': require ('./js/acx.js'), 'anxpro': require ('./js/anxpro.js'), 'aofex': require ('./js/aofex.js'), 'bcex': require ('./js/bcex.js'), 'bequant': require ('./js/bequant.js'), 'bibox': require ('./js/bibox.js'), 'bigone': require ('./js/bigone.js'), 'binance': require ('./js/binance.js'), 'binanceje': require ('./js/binanceje.js'), 'binanceus': require ('./js/binanceus.js'), 'bit2c': require ('./js/bit2c.js'), 'bitbank': require ('./js/bitbank.js'), 'bitbay': require ('./js/bitbay.js'), 'bitfinex': require ('./js/bitfinex.js'), 'bitfinex2': require ('./js/bitfinex2.js'), 'bitflyer': require ('./js/bitflyer.js'), 'bitforex': require ('./js/bitforex.js'), 'bithumb': require ('./js/bithumb.js'), 'bitkk': require ('./js/bitkk.js'), 'bitmart': require ('./js/bitmart.js'), 'bitmax': require ('./js/bitmax.js'), 'bitmex': require ('./js/bitmex.js'), 'bitso': require ('./js/bitso.js'), 'bitstamp': require ('./js/bitstamp.js'), 'bitstamp1': require ('./js/bitstamp1.js'), 'bittrex': require ('./js/bittrex.js'), 'bitz': require ('./js/bitz.js'), 'bl3p': require ('./js/bl3p.js'), 'bleutrade': require ('./js/bleutrade.js'), 'braziliex': require ('./js/braziliex.js'), 'btcalpha': require ('./js/btcalpha.js'), 'btcbox': require ('./js/btcbox.js'), 'btcmarkets': require ('./js/btcmarkets.js'), 'btctradeim': require ('./js/btctradeim.js'), 'btctradeua': require ('./js/btctradeua.js'), 'btcturk': require ('./js/btcturk.js'), 'buda': require ('./js/buda.js'), 'bw': require ('./js/bw.js'), 'bybit': require ('./js/bybit.js'), 'bytetrade': require ('./js/bytetrade.js'), 'cex': require ('./js/cex.js'), 'chilebit': require ('./js/chilebit.js'), 'coinbase': require ('./js/coinbase.js'), 'coinbaseprime': require ('./js/coinbaseprime.js'), 'coinbasepro': require ('./js/coinbasepro.js'), 'coincheck': require ('./js/coincheck.js'), 'coinegg': require ('./js/coinegg.js'), 'coinex': require ('./js/coinex.js'), 'coinfalcon': require ('./js/coinfalcon.js'), 'coinfloor': require ('./js/coinfloor.js'), 'coingi': require ('./js/coingi.js'), 'coinmarketcap': require ('./js/coinmarketcap.js'), 'coinmate': require ('./js/coinmate.js'), 'coinone': require ('./js/coinone.js'), 'coinspot': require ('./js/coinspot.js'), 'coolcoin': require ('./js/coolcoin.js'), 'coss': require ('./js/coss.js'), 'crex24': require ('./js/crex24.js'), 'deribit': require ('./js/deribit.js'), 'digifinex': require ('./js/digifinex.js'), 'dsx': require ('./js/dsx.js'), 'exmo': require ('./js/exmo.js'), 'exx': require ('./js/exx.js'), 'fcoin': require ('./js/fcoin.js'), 'fcoinjp': require ('./js/fcoinjp.js'), 'flowbtc': require ('./js/flowbtc.js'), 'foxbit': require ('./js/foxbit.js'), 'ftx': require ('./js/ftx.js'), 'fybse': require ('./js/fybse.js'), 'gateio': require ('./js/gateio.js'), 'gemini': require ('./js/gemini.js'), 'hbtc': require ('./js/hbtc.js'), 'hitbtc': require ('./js/hitbtc.js'), 'hollaex': require ('./js/hollaex.js'), 'huobipro': require ('./js/huobipro.js'), 'huobiru': require ('./js/huobiru.js'), 'ice3x': require ('./js/ice3x.js'), 'idex': require ('./js/idex.js'), 'independentreserve': require ('./js/independentreserve.js'), 'indodax': require ('./js/indodax.js'), 'itbit': require ('./js/itbit.js'), 'kkex': require ('./js/kkex.js'), 'kraken': require ('./js/kraken.js'), 'kucoin': require ('./js/kucoin.js'), 'kuna': require ('./js/kuna.js'), 'lakebtc': require ('./js/lakebtc.js'), 'latoken': require ('./js/latoken.js'), 'lbank': require ('./js/lbank.js'), 'liquid': require ('./js/liquid.js'), 'livecoin': require ('./js/livecoin.js'), 'luno': require ('./js/luno.js'), 'lykke': require ('./js/lykke.js'), 'mercado': require ('./js/mercado.js'), 'mixcoins': require ('./js/mixcoins.js'), 'oceanex': require ('./js/oceanex.js'), 'okcoin': require ('./js/okcoin.js'), 'okex': require ('./js/okex.js'), 'paymium': require ('./js/paymium.js'), 'poloniex': require ('./js/poloniex.js'), 'rightbtc': require ('./js/rightbtc.js'), 'southxchange': require ('./js/southxchange.js'), 'stex': require ('./js/stex.js'), 'stronghold': require ('./js/stronghold.js'), 'surbitcoin': require ('./js/surbitcoin.js'), 'theocean': require ('./js/theocean.js'), 'therock': require ('./js/therock.js'), 'tidebit': require ('./js/tidebit.js'), 'tidex': require ('./js/tidex.js'), 'timex': require ('./js/timex.js'), 'topq': require ('./js/topq.js'), 'upbit': require ('./js/upbit.js'), 'vaultoro': require ('./js/vaultoro.js'), 'vbtc': require ('./js/vbtc.js'), 'whitebit': require ('./js/whitebit.js'), 'xbtce': require ('./js/xbtce.js'), 'yobit': require ('./js/yobit.js'), 'zaif': require ('./js/zaif.js'), 'zb': require ('./js/zb.js'), } //----------------------------------------------------------------------------- module.exports = Object.assign ({ version, Exchange, 'exchanges': Object.keys (exchanges) }, exchanges, functions, errors) //----------------------------------------------------------------------------- },{"./js/_1btcxe.js":3,"./js/acx.js":4,"./js/anxpro.js":5,"./js/aofex.js":6,"./js/base/Exchange":7,"./js/base/errors":9,"./js/base/functions":10,"./js/bcex.js":21,"./js/bequant.js":22,"./js/bibox.js":23,"./js/bigone.js":24,"./js/binance.js":25,"./js/binanceje.js":26,"./js/binanceus.js":27,"./js/bit2c.js":28,"./js/bitbank.js":29,"./js/bitbay.js":30,"./js/bitfinex.js":31,"./js/bitfinex2.js":32,"./js/bitflyer.js":33,"./js/bitforex.js":34,"./js/bithumb.js":35,"./js/bitkk.js":36,"./js/bitmart.js":37,"./js/bitmax.js":38,"./js/bitmex.js":39,"./js/bitso.js":40,"./js/bitstamp.js":41,"./js/bitstamp1.js":42,"./js/bittrex.js":43,"./js/bitz.js":44,"./js/bl3p.js":45,"./js/bleutrade.js":46,"./js/braziliex.js":47,"./js/btcalpha.js":48,"./js/btcbox.js":49,"./js/btcmarkets.js":50,"./js/btctradeim.js":51,"./js/btctradeua.js":52,"./js/btcturk.js":53,"./js/buda.js":54,"./js/bw.js":55,"./js/bybit.js":56,"./js/bytetrade.js":57,"./js/cex.js":58,"./js/chilebit.js":59,"./js/coinbase.js":60,"./js/coinbaseprime.js":61,"./js/coinbasepro.js":62,"./js/coincheck.js":63,"./js/coinegg.js":64,"./js/coinex.js":65,"./js/coinfalcon.js":66,"./js/coinfloor.js":67,"./js/coingi.js":68,"./js/coinmarketcap.js":69,"./js/coinmate.js":70,"./js/coinone.js":71,"./js/coinspot.js":72,"./js/coolcoin.js":73,"./js/coss.js":74,"./js/crex24.js":75,"./js/deribit.js":76,"./js/digifinex.js":77,"./js/dsx.js":78,"./js/exmo.js":79,"./js/exx.js":80,"./js/fcoin.js":81,"./js/fcoinjp.js":82,"./js/flowbtc.js":83,"./js/foxbit.js":84,"./js/ftx.js":85,"./js/fybse.js":86,"./js/gateio.js":87,"./js/gemini.js":88,"./js/hbtc.js":89,"./js/hitbtc.js":90,"./js/hollaex.js":91,"./js/huobipro.js":92,"./js/huobiru.js":93,"./js/ice3x.js":94,"./js/idex.js":95,"./js/independentreserve.js":96,"./js/indodax.js":97,"./js/itbit.js":98,"./js/kkex.js":99,"./js/kraken.js":100,"./js/kucoin.js":101,"./js/kuna.js":102,"./js/lakebtc.js":103,"./js/latoken.js":104,"./js/lbank.js":105,"./js/liquid.js":106,"./js/livecoin.js":107,"./js/luno.js":108,"./js/lykke.js":109,"./js/mercado.js":110,"./js/mixcoins.js":111,"./js/oceanex.js":112,"./js/okcoin.js":113,"./js/okex.js":114,"./js/paymium.js":115,"./js/poloniex.js":116,"./js/rightbtc.js":117,"./js/southxchange.js":118,"./js/stex.js":156,"./js/stronghold.js":157,"./js/surbitcoin.js":158,"./js/theocean.js":159,"./js/therock.js":160,"./js/tidebit.js":161,"./js/tidex.js":162,"./js/timex.js":163,"./js/topq.js":164,"./js/upbit.js":165,"./js/vaultoro.js":166,"./js/vbtc.js":167,"./js/whitebit.js":168,"./js/xbtce.js":169,"./js/yobit.js":170,"./js/zaif.js":171,"./js/zb.js":172}],3:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ExchangeNotAvailable } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class _1btcxe extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': '_1btcxe', 'name': '1BTCXE', 'countries': [ 'PA' ], // Panama 'comment': 'Crypto Capital API', 'has': { 'CORS': true, 'withdraw': true, }, 'timeframes': { '1d': '1year', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766049-2b294408-5ecc-11e7-85cc-adaff013dc1a.jpg', 'api': 'https://1btcxe.com/api', 'www': 'https://1btcxe.com', 'doc': 'https://1btcxe.com/api-docs.php', }, 'api': { 'public': { 'get': [ 'stats', 'historical-prices', 'order-book', 'transactions', ], }, 'private': { 'post': [ 'balances-and-info', 'open-orders', 'user-transactions', 'btc-deposit-address/get', 'btc-deposit-address/new', 'deposits/get', 'withdrawals/get', 'orders/new', 'orders/edit', 'orders/cancel', 'orders/status', 'withdrawals/new', ], }, }, }); } async fetchMarkets (params = {}) { return [ { 'id': 'USD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'BTC', 'quoteId': 'USD' }, { 'id': 'EUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR' }, { 'id': 'CNY', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'baseId': 'BTC', 'quoteId': 'CNY' }, { 'id': 'RUB', 'symbol': 'BTC/RUB', 'base': 'BTC', 'quote': 'RUB', 'baseId': 'BTC', 'quoteId': 'RUB' }, { 'id': 'CHF', 'symbol': 'BTC/CHF', 'base': 'BTC', 'quote': 'CHF', 'baseId': 'BTC', 'quoteId': 'CHF' }, { 'id': 'JPY', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'BTC', 'quoteId': 'JPY' }, { 'id': 'GBP', 'symbol': 'BTC/GBP', 'base': 'BTC', 'quote': 'GBP', 'baseId': 'BTC', 'quoteId': 'GBP' }, { 'id': 'CAD', 'symbol': 'BTC/CAD', 'base': 'BTC', 'quote': 'CAD', 'baseId': 'BTC', 'quoteId': 'CAD' }, { 'id': 'AUD', 'symbol': 'BTC/AUD', 'base': 'BTC', 'quote': 'AUD', 'baseId': 'BTC', 'quoteId': 'AUD' }, { 'id': 'AED', 'symbol': 'BTC/AED', 'base': 'BTC', 'quote': 'AED', 'baseId': 'BTC', 'quoteId': 'AED' }, { 'id': 'BGN', 'symbol': 'BTC/BGN', 'base': 'BTC', 'quote': 'BGN', 'baseId': 'BTC', 'quoteId': 'BGN' }, { 'id': 'CZK', 'symbol': 'BTC/CZK', 'base': 'BTC', 'quote': 'CZK', 'baseId': 'BTC', 'quoteId': 'CZK' }, { 'id': 'DKK', 'symbol': 'BTC/DKK', 'base': 'BTC', 'quote': 'DKK', 'baseId': 'BTC', 'quoteId': 'DKK' }, { 'id': 'HKD', 'symbol': 'BTC/HKD', 'base': 'BTC', 'quote': 'HKD', 'baseId': 'BTC', 'quoteId': 'HKD' }, { 'id': 'HRK', 'symbol': 'BTC/HRK', 'base': 'BTC', 'quote': 'HRK', 'baseId': 'BTC', 'quoteId': 'HRK' }, { 'id': 'HUF', 'symbol': 'BTC/HUF', 'base': 'BTC', 'quote': 'HUF', 'baseId': 'BTC', 'quoteId': 'HUF' }, { 'id': 'ILS', 'symbol': 'BTC/ILS', 'base': 'BTC', 'quote': 'ILS', 'baseId': 'BTC', 'quoteId': 'ILS' }, { 'id': 'INR', 'symbol': 'BTC/INR', 'base': 'BTC', 'quote': 'INR', 'baseId': 'BTC', 'quoteId': 'INR' }, { 'id': 'MUR', 'symbol': 'BTC/MUR', 'base': 'BTC', 'quote': 'MUR', 'baseId': 'BTC', 'quoteId': 'MUR' }, { 'id': 'MXN', 'symbol': 'BTC/MXN', 'base': 'BTC', 'quote': 'MXN', 'baseId': 'BTC', 'quoteId': 'MXN' }, { 'id': 'NOK', 'symbol': 'BTC/NOK', 'base': 'BTC', 'quote': 'NOK', 'baseId': 'BTC', 'quoteId': 'NOK' }, { 'id': 'NZD', 'symbol': 'BTC/NZD', 'base': 'BTC', 'quote': 'NZD', 'baseId': 'BTC', 'quoteId': 'NZD' }, { 'id': 'PLN', 'symbol': 'BTC/PLN', 'base': 'BTC', 'quote': 'PLN', 'baseId': 'BTC', 'quoteId': 'PLN' }, { 'id': 'RON', 'symbol': 'BTC/RON', 'base': 'BTC', 'quote': 'RON', 'baseId': 'BTC', 'quoteId': 'RON' }, { 'id': 'SEK', 'symbol': 'BTC/SEK', 'base': 'BTC', 'quote': 'SEK', 'baseId': 'BTC', 'quoteId': 'SEK' }, { 'id': 'SGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD', 'baseId': 'BTC', 'quoteId': 'SGD' }, { 'id': 'THB', 'symbol': 'BTC/THB', 'base': 'BTC', 'quote': 'THB', 'baseId': 'BTC', 'quoteId': 'THB' }, { 'id': 'TRY', 'symbol': 'BTC/TRY', 'base': 'BTC', 'quote': 'TRY', 'baseId': 'BTC', 'quoteId': 'TRY' }, { 'id': 'ZAR', 'symbol': 'BTC/ZAR', 'base': 'BTC', 'quote': 'ZAR', 'baseId': 'BTC', 'quoteId': 'ZAR' }, ]; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostBalancesAndInfo (params); const balance = response['balances-and-info']; const result = { 'info': balance }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currency = this.currency (code); const currencyId = currency['id']; const account = this.account (); account['free'] = this.safeFloat (balance['available'], currencyId); account['used'] = this.safeFloat (balance['on_hold'], currencyId); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'currency': this.marketId (symbol), }; const response = await this.publicGetOrderBook (this.extend (request, params)); return this.parseOrderBook (response['order-book'], undefined, 'bid', 'ask', 'price', 'order_amount'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'currency': this.marketId (symbol), }; const response = await this.publicGetStats (this.extend (request, params)); const ticker = this.safeValue (response, 'stats', {}); const last = this.safeFloat (ticker, 'last_price'); return { 'symbol': symbol, 'timestamp': undefined, 'datetime': undefined, 'high': this.safeFloat (ticker, 'max'), 'low': this.safeFloat (ticker, 'min'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeFloat (ticker, 'daily_change'), 'percentage': undefined, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeFloat (ticker, 'total_btc_traded'), 'info': ticker, }; } parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { return [ this.parse8601 (ohlcv['date'] + ' 00:00:00'), undefined, undefined, undefined, this.safeFloat (ohlcv, 'price'), undefined, ]; } async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const response = await this.publicGetHistoricalPrices (this.extend ({ 'currency': market['id'], 'timeframe': this.timeframes[timeframe], }, params)); const ohlcvs = this.toArray (this.omit (response['historical-prices'], 'request_currency')); return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'timestamp'); const id = this.safeString (trade, 'id'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const type = undefined; const side = this.safeString (trade, 'maker_type'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetTransactions (this.extend (request, params)); const trades = this.toArray (this.omit (response['transactions'], 'request_currency')); return this.parseTrades (trades, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'side': side, 'type': type, 'currency': this.marketId (symbol), 'amount': amount, }; if (type === 'limit') { request['limit_price'] = price; } const result = await this.privatePostOrdersNew (this.extend (request, params)); return { 'info': result, 'id': result, }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; return await this.privatePostOrdersCancel (this.extend (request, params)); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'amount': parseFloat (amount), 'address': address, }; const response = await this.privatePostWithdrawalsNew (this.extend (request, params)); return { 'info': response, 'id': response['result']['uuid'], }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { if (this.id === 'cryptocapital') { throw new ExchangeError (this.id + ' is an abstract base API for _1btcxe'); } let url = this.urls['api'] + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); const query = this.extend ({ 'api_key': this.apiKey, 'nonce': this.nonce (), }, params); const request = this.json (query); query['signature'] = this.hmac (this.encode (request), this.encode (this.secret)); body = this.json (query); headers = { 'Content-Type': 'application/json' }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if (typeof response === 'string') { if (response.indexOf ('Maintenance') >= 0) { throw new ExchangeNotAvailable (this.id + ' on maintenance'); } } if ('errors' in response) { let errors = []; for (let e = 0; e < response['errors'].length; e++) { const error = response['errors'][e]; errors.push (error['code'] + ': ' + error['message']); } errors = errors.join (' '); throw new ExchangeError (this.id + ' ' + errors); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],4:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { InsufficientFunds, OrderNotFound } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class acx extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'acx', 'name': 'ACX', 'countries': [ 'AU' ], 'rateLimit': 1000, 'version': 'v2', 'has': { 'CORS': true, 'fetchTickers': true, 'fetchOHLCV': true, 'withdraw': true, 'fetchOrder': true, }, 'timeframes': { '1m': '1', '5m': '5', '15m': '15', '30m': '30', '1h': '60', '2h': '120', '4h': '240', '12h': '720', '1d': '1440', '3d': '4320', '1w': '10080', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/30247614-1fe61c74-9621-11e7-9e8c-f1a627afa279.jpg', 'extension': '.json', 'api': 'https://acx.io/api', 'www': 'https://acx.io', 'doc': 'https://acx.io/documents/api_v2', }, 'api': { 'public': { 'get': [ 'depth', // Get depth or specified market Both asks and bids are sorted from highest price to lowest. 'k_with_pending_trades', // Get K data with pending trades, which are the trades not included in K data yet, because there's delay between trade generated and processed by K data generator 'k', // Get OHLC(k line) of specific market 'markets', // Get all available markets 'order_book', // Get the order book of specified market 'order_book/{market}', 'tickers', // Get ticker of all markets 'tickers/{market}', // Get ticker of specific market 'timestamp', // Get server current time, in seconds since Unix epoch 'trades', // Get recent trades on market, each trade is included only once Trades are sorted in reverse creation order. 'trades/{market}', ], }, 'private': { 'get': [ 'members/me', // Get your profile and accounts info 'deposits', // Get your deposits history 'deposit', // Get details of specific deposit 'deposit_address', // Where to deposit The address field could be empty when a new address is generating (e.g. for bitcoin), you should try again later in that case. 'orders', // Get your orders, results is paginated 'order', // Get information of specified order 'trades/my', // Get your executed trades Trades are sorted in reverse creation order. 'withdraws', // Get your cryptocurrency withdraws 'withdraw', // Get your cryptocurrency withdraw ], 'post': [ 'orders', // Create a Sell/Buy order 'orders/multi', // Create multiple sell/buy orders 'orders/clear', // Cancel all my orders 'order/delete', // Cancel an order 'withdraw', // Create a withdraw ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.2 / 100, 'taker': 0.2 / 100, }, 'funding': { 'tierBased': false, 'percentage': true, 'withdraw': {}, // There is only 1% fee on withdrawals to your bank account. }, }, 'commonCurrencies': { 'PLA': 'Plair', }, 'exceptions': { '2002': InsufficientFunds, '2003': OrderNotFound, }, }); } async fetchMarkets (params = {}) { const markets = await this.publicGetMarkets (params); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = market['id']; const symbol = market['name']; let baseId = this.safeString (market, 'base_unit'); let quoteId = this.safeString (market, 'quote_unit'); if ((baseId === undefined) || (quoteId === undefined)) { const ids = symbol.split ('/'); baseId = ids[0].toLowerCase (); quoteId = ids[1].toLowerCase (); } let base = baseId.toUpperCase (); let quote = quoteId.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); // todo: find out their undocumented precision and limits const precision = { 'amount': 8, 'price': 8, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'info': market, 'active': undefined, 'limits': this.limits, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetMembersMe (params); const balances = this.safeValue (response, 'accounts'); const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'locked'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = 300 } const orderbook = await this.publicGetDepth (this.extend (request, params)); const timestamp = this.safeTimestamp (orderbook, 'timestamp'); return this.parseOrderBook (orderbook, timestamp); } parseTicker (ticker, market = undefined) { const timestamp = this.safeTimestamp (ticker, 'at'); ticker = ticker['ticker']; let symbol = undefined; if (market) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickers (params); const ids = Object.keys (response); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; let market = undefined; let symbol = id; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } else { let base = id.slice (0, 3); let quote = id.slice (3, 6); base = base.toUpperCase (); quote = quote.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); symbol = base + '/' + quote; } result[symbol] = this.parseTicker (response[id], market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTickersMarket (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); const id = this.safeString (trade, 'tid'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, 'order': undefined, 'takerOrMaker': undefined, 'price': this.safeFloat (trade, 'price'), 'amount': this.safeFloat (trade, 'volume'), 'cost': this.safeFloat (trade, 'funds'), 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, ohlcv[1], ohlcv[2], ohlcv[3], ohlcv[4], ohlcv[5], ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 500; // default is 30 } const request = { 'market': market['id'], 'period': this.timeframes[timeframe], 'limit': limit, }; if (since !== undefined) { request['timestamp'] = parseInt (since / 1000); } const response = await this.publicGetK (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } parseOrderStatus (status) { const statuses = { 'done': 'closed', 'wait': 'open', 'cancel': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } else { const marketId = this.safeString (order, 'market'); symbol = this.markets_by_id[marketId]['symbol']; } const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const status = this.parseOrderStatus (this.safeString (order, 'state')); const type = this.safeString (order, 'type'); const side = this.safeString (order, 'side'); const id = this.safeString (order, 'id'); return { 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': this.safeFloat (order, 'price'), 'amount': this.safeFloat (order, 'volume'), 'filled': this.safeFloat (order, 'executed_volume'), 'remaining': this.safeFloat (order, 'remaining_volume'), 'trades': undefined, 'fee': undefined, 'info': order, 'cost': undefined, 'average': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': parseInt (id), }; const response = await this.privateGetOrder (this.extend (request, params)); return this.parseOrder (response); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), 'side': side, 'volume': amount.toString (), 'ord_type': type, }; if (type === 'limit') { request['price'] = price.toString (); } const response = await this.privatePostOrders (this.extend (request, params)); const marketId = this.safeValue (response, 'market'); const market = this.safeValue (this.markets_by_id, marketId); return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privatePostOrderDelete (this.extend (request, params)); const order = this.parseOrder (response); const status = order['status']; if (status === 'closed' || status === 'canceled') { throw new OrderNotFound (this.id + ' ' + this.json (order)); } return order; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); // they have XRP but no docs on memo/tag const request = { 'currency': currency['id'], 'sum': amount, 'address': address, }; const response = await this.privatePostWithdraw (this.extend (request, params)); // withdrawal response is undocumented return { 'info': response, 'id': undefined, }; } nonce () { return this.milliseconds (); } encodeParams (params) { if ('orders' in params) { const orders = params['orders']; let query = this.urlencode (this.keysort (this.omit (params, 'orders'))); for (let i = 0; i < orders.length; i++) { const order = orders[i]; const keys = Object.keys (order); for (let k = 0; k < keys.length; k++) { const key = keys[k]; const value = order[key]; query += '&orders%5B%5D%5B' + key + '%5D=' + value.toString (); } } return query; } return this.urlencode (this.keysort (params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/api/' + this.version + '/' + this.implodeParams (path, params); if ('extension' in this.urls) { request += this.urls['extension']; } const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'] + request; if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const query = this.encodeParams (this.extend ({ 'access_key': this.apiKey, 'tonce': nonce, }, params)); const auth = method + '|' + request + '|' + query; const signed = this.hmac (this.encode (auth), this.encode (this.secret)); const suffix = query + '&signature=' + signed; if (method === 'GET') { url += '?' + suffix; } else { body = suffix; headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } if (code === 400) { const error = this.safeValue (response, 'error'); const errorCode = this.safeString (error, 'code'); const feedback = this.id + ' ' + this.json (response); this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); // fallback to default error handler } } }; },{"./base/Exchange":7,"./base/errors":9}],5:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, BadRequest, OrderNotFound, NotSupported } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class anxpro extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'anxpro', 'name': 'ANXPro', 'countries': [ 'JP', 'SG', 'HK', 'NZ' ], 'rateLimit': 1500, 'userAgent': this.userAgents['chrome'], 'has': { 'CORS': false, 'fetchCurrencies': true, 'fetchOHLCV': false, 'fetchTrades': false, 'fetchOpenOrders': true, 'fetchDepositAddress': true, 'fetchTransactions': true, 'fetchMyTrades': true, 'createDepositAddress': false, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27765983-fd8595da-5ec9-11e7-82e3-adb3ab8c2612.jpg', 'api': { 'public': 'https://anxpro.com/api/2', 'private': 'https://anxpro.com/api/2', 'v3public': 'https://anxpro.com/api/3', 'v3private': 'https://anxpro.com/api/3', }, 'www': 'https://anxpro.com', 'doc': [ 'https://anxv2.docs.apiary.io', 'https://anxv3.docs.apiary.io', 'https://anxpro.com/pages/api', ], }, 'api': { 'v3public': { 'get': [ 'currencyStatic', ], }, 'v3private': { 'post': [ 'register/register', 'register/verifyRegistration', 'register/resendVerification', 'register/autoRegister', 'account', 'subaccount/new', 'transaction/list', 'order/list', 'trade/list', 'send', 'receive', 'receive/create', 'batch/new', 'batch/add', 'batch/list', 'batch/info', 'batch/closeForSend', 'order/new', 'order/info', 'order/cancel', 'retail/quote', 'retail/trade', 'validateAddress', 'address/check', 'alert/create', 'alert/delete', 'alert/list', 'kyc/personal', 'kyc/document', 'kyc/status', 'kyc/verifyCode', 'news/list', 'press/list', 'announcements/list', 'apiDoc/list', ], }, 'public': { 'get': [ '{currency_pair}/money/ticker', '{currency_pair}/money/depth/full', '{currency_pair}/money/trade/fetch', // disabled by ANXPro ], }, 'private': { 'post': [ '{currency_pair}/money/order/add', '{currency_pair}/money/order/cancel', '{currency_pair}/money/order/quote', '{currency_pair}/money/order/result', '{currency_pair}/money/orders', 'money/{currency}/address', 'money/{currency}/send_simple', 'money/info', 'money/trade/list', 'money/wallet/history', ], }, }, 'httpExceptions': { '403': AuthenticationError, }, 'exceptions': { 'exact': { // v2 'Insufficient Funds': InsufficientFunds, 'Trade value too small': InvalidOrder, 'The currency pair is not supported': BadRequest, 'Order amount is too low': InvalidOrder, 'Order amount is too high': InvalidOrder, 'order rate is too low': InvalidOrder, 'order rate is too high': InvalidOrder, 'Too many open orders': InvalidOrder, 'Unexpected error': ExchangeError, 'Order Engine is offline': ExchangeNotAvailable, 'No executed order with that identifer found': OrderNotFound, 'Unknown server error, please contact support.': ExchangeError, 'Not available': ExchangeNotAvailable, // { "status": "Not available" } }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.1 / 100, 'taker': 0.2 / 100, }, }, 'options': { 'fetchMyTradesMethod': 'private_post_money_trade_list', // or 'v3private_post_trade_list' }, }); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { // todo: migrate this to fetchLedger await this.loadMarkets (); const request = {}; if (since !== undefined) { request['from'] = since; } if (limit !== undefined) { request['max'] = limit; } const currency = (code === undefined) ? undefined : this.currency (code); if (currency !== undefined) { request['ccy'] = currency['id']; } const response = await this.v3privatePostTransactionList (this.extend (request, params)); // // { // transactions: [ // { // transactionClass: 'COIN', // uuid: '7896857b-2ed6-4c62-ba4c-619837438d9c', // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', // amount: -17865.72689976, // fee: 1, // balanceBefore: 17865.72689976, // balanceAfter: 17865.72689976, // ccy: 'XRP', // transactionState: 'PROCESSED', // transactionType: 'WITHDRAWAL', // received: '1551357946000', // processed: '1551357946000', // timestampMillis: '1557441435932', // displayTitle: 'Coin Withdrawal', // displayDescription: 'Withdraw to: rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345', // coinAddress: 'rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345', // coinTransactionId: '68444611753E9D8F5C33DCBBF43F01391070F79CAFCF7625397D1CEFA519064A', // subAccount: [ // Object // ] // }, // { // transactionClass: 'FILL', // uuid: 'a5ae54de-c14a-4ef8-842d-56000c9dc7ab', // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', // amount: 0.09006364, // fee: 0.00018013, // balanceBefore: 0.3190001, // balanceAfter: 0.40888361, // ccy: 'BTC', // transactionState: 'PROCESSED', // transactionType: 'FILL_CREDIT', // received: '1551357057000', // processed: '1551357057000', // timestampMillis: '1557441435956', // displayTitle: 'Order Fill', // displayDescription: 'Buy BTC @ 3008.53930 EUR/BTC' // } // ], // count: ..., // timestamp: '1557441435971', // resultCode: 'OK' // } // const transactions = this.safeValue (response, 'transactions', []); const grouped = this.groupBy (transactions, 'transactionType', []); const depositsAndWithdrawals = this.arrayConcat (this.safeValue (grouped, 'DEPOSIT', []), this.safeValue (grouped, 'WITHDRAWAL', [])); return this.parseTransactions (depositsAndWithdrawals, currency, since, limit); } parseTransaction (transaction, currency = undefined) { // // withdrawal // // { // transactionClass: 'COIN', // uuid: 'bff91938-4dad-4c48-9db6-468324ce96c1', // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', // amount: -0.40888361, // fee: 0.002, // balanceBefore: 0.40888361, // balanceAfter: 0.40888361, // ccy: 'BTC', // transactionState: 'PROCESSED', // transactionType: 'WITHDRAWAL', // received: '1551357156000', // processed: '1551357156000', // timestampMillis: '1557441846213', // displayTitle: 'Coin Withdrawal', // displayDescription: 'Withdraw to: 1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', // coinAddress: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', // coinTransactionId: // 'ab80abcb62bf6261ebc827c73dd59a4ce15d740b6ba734af6542f43b6485b923', // subAccount: { // uuid: '652e1add-0d0b-462c-a03c-d6197c825c1a', // name: 'DEFAULT' // } // } // // deposit // // { // "transactionClass": "COIN", // "uuid": "eb65576f-c1a8-423c-8e2f-fa50109b2eab", // "userUuid": "82027ee9-cb59-4f29-80d6-f7e793f39ad4", // "amount": 3.99287184, // "fee": 0, // "balanceBefore": 8.39666034, // "balanceAfter": 12.38953218, // "ccy": "ETH", // "transactionState": "PROCESSED", // "transactionType": "DEPOSIT", // "received": "1529420056000", // "processed": "1529420766000", // "timestampMillis": "1557442743854", // "displayTitle": "Coin Deposit", // "displayDescription": "Deposit to: 0xf123aa44fadea913a7da99cc2ee202db684ce0e3", // "coinTransactionId": "0x33a3e5ea7c034dc5324a88aa313962df0a5d571ab4bcc3cb00b876b1bdfc54f7", // "coinConfirmations": 51, // "coinConfirmationsRequired": 45, // "subAccount": {"uuid": "aba1de05-c7c6-49d7-84ab-a6aca0e827b6", "name": "DEFAULT"} // } // const timestamp = this.safeInteger (transaction, 'received'); const updated = this.safeInteger (transaction, 'processed'); const transactionType = this.safeString (transaction, 'transactionType'); let type = undefined; let amount = this.safeFloat (transaction, 'amount'); let address = this.safeString (transaction, 'coinAddress'); let tag = undefined; if (transactionType === 'WITHDRAWAL') { type = 'withdrawal'; amount = -amount; if (address) { // xrp: "coinAddress": "rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345", if (address.indexOf ('?dt=') >= 0) { const parts = address.split ('?dt='); address = parts[0]; tag = parts[1]; } } } else if (transactionType === 'DEPOSIT') { if (!address) { const displayDescription = this.safeString (transaction, 'displayDescription'); const addressText = displayDescription.replace ('Deposit to: ', ''); if (addressText.length > 0) { // eth: "displayDescription": "Deposit to: 0xf123aa44fadea913a7da99cc2ee202db684ce0e3", // xrp: "displayDescription": "Deposit to: rUjxty1WWLwX1evhKf3C2XNZDMcXEZ9ToJ?dt=504562345", if (addressText.indexOf ('?dt=') >= 0) { const parts = addressText.split ('?dt='); address = parts[0]; tag = parts[1]; } else { address = addressText; } } } type = 'deposit'; } const currencyId = this.safeString (transaction, 'ccy'); const code = this.safeCurrencyCode (currencyId); const transactionState = this.safeString (transaction, 'transactionState'); const status = this.parseTransactionStatus (transactionState); const feeCost = this.safeFloat (transaction, 'fee'); const netAmount = amount - feeCost; return { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'id': this.safeString (transaction, 'uuid'), 'currency': code, 'amount': netAmount, 'address': address, 'tag': tag, 'status': status, 'type': type, 'updated': updated, 'txid': this.safeString (transaction, 'coinTransactionId'), 'fee': { 'cost': feeCost, 'currency': code, }, 'info': transaction, }; } parseTransactionStatus (status) { const statuses = { 'PROCESSED': 'ok', 'REVERSED': 'canceled', 'CANCELLED_INSUFFICIENT_FUNDS': 'canceled', 'CANCELLED_LIMIT_BREACH': 'canceled', }; return this.safeString (statuses, status, status); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); // // v2 // // { // result: 'success', // data: [ // { // tradeId: 'c2ed821d-717a-4b7e-beb0-a9ba60e8f5a0', // orderId: '5a65ae21-c7a8-4009-b3af-306c2ad21a02', // timestamp: '1551357057000', // tradedCurrencyFillAmount: '0.09006364', // settlementCurrencyFillAmount: '270.96', // settlementCurrencyFillAmountUnrounded: '270.96000000', // price: '3008.53930', // ccyPair: 'BTCEUR', // side: 'BUY' // missing in v3 // }, // { // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', // timestamp: '1551357033000', // tradedCurrencyFillAmount: '0.06521746', // settlementCurrencyFillAmount: '224.09', // settlementCurrencyFillAmountUnrounded: '224.09000000', // price: '3436.04305', // ccyPair: 'BTCUSD', // side: 'BUY' // missing in v3 // }, // ] // } // // v3 // // { // trades: [ // { // tradeId: 'c2ed821d-717a-4b7e-beb0-a9ba60e8f5a0', // orderId: '5a65ae21-c7a8-4009-b3af-306c2ad21a02', // timestamp: '1551357057000', // tradedCurrencyFillAmount: '0.09006364', // settlementCurrencyFillAmount: '270.96', // settlementCurrencyFillAmountUnrounded: '270.96000000', // price: '3008.53930', // ccyPair: 'BTCEUR' // }, // { // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', // timestamp: '1551357033000', // tradedCurrencyFillAmount: '0.06521746', // settlementCurrencyFillAmount: '224.09', // settlementCurrencyFillAmountUnrounded: '224.09000000', // price: '3436.04305', // ccyPair: 'BTCUSD' // }, // ], // count: 3, // timestamp: '1557438456732', // resultCode: 'OK' // } // const request = {}; if (limit !== undefined) { request['max'] = limit; } const method = this.safeString (this.options, 'fetchMyTradesMethod', 'private_post_money_trade_list'); const response = await this[method] (this.extend (request, params)); const trades = this.safeValue2 (response, 'trades', 'data', []); const market = (symbol === undefined) ? undefined : this.market (symbol); return this.parseTrades (trades, market, since, limit); } parseTrade (trade, market = undefined) { // // v2 // // { // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', // timestamp: '1551357033000', // tradedCurrencyFillAmount: '0.06521746', // settlementCurrencyFillAmount: '224.09', // settlementCurrencyFillAmountUnrounded: '224.09000000', // price: '3436.04305', // ccyPair: 'BTCUSD', // side: 'BUY', // missing in v3 // } // // v3 // // { // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', // timestamp: '1551357033000', // tradedCurrencyFillAmount: '0.06521746', // settlementCurrencyFillAmount: '224.09', // settlementCurrencyFillAmountUnrounded: '224.09000000', // price: '3436.04305', // ccyPair: 'BTCUSD' // } // const id = this.safeString (trade, 'tradeId'); const orderId = this.safeString (trade, 'orderId'); const timestamp = this.safeInteger (trade, 'timestamp'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'tradedCurrencyFillAmount'); const cost = this.safeFloat (trade, 'settlementCurrencyFillAmount'); const side = this.safeStringLower (trade, 'side'); let symbol = undefined; const marketId = this.safeString (trade, 'ccyPair'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } return { 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, 'info': trade, 'takerOrMaker': undefined, }; } async fetchCurrencies (params = {}) { const response = await this.v3publicGetCurrencyStatic (params); // // { // "currencyStatic": { // "currencies": { // "HKD": { // "decimals": 2, // "minOrderSize": 1.00000000, // "maxOrderSize": 10000000000.00000000, // "displayDenominator": 1, // "summaryDecimals": 0, // "displayUnit": "HKD", // "symbol": "$", // "type": "FIAT", // "engineSettings": { // "depositsEnabled": false, // "withdrawalsEnabled": true, // "displayEnabled": true, // "mobileAccessEnabled": true // }, // "minOrderValue": 1.00000000, // "maxOrderValue": 10000000000.00000000, // "maxMarketOrderValue": 36000.00000000, // "maxMarketOrderSize": 36000.00000000, // "assetDivisibility": 0 // }, // "ETH": { // "decimals": 8, // "minOrderSize": 0.00010000, // "maxOrderSize": 1000000000.00000000, // "type": "CRYPTO", // "confirmationThresholds": [ // { "confosRequired": 30, "threshold": 0.50000000 }, // { "confosRequired": 45, "threshold": 10.00000000 }, // { "confosRequired": 70 } // ], // "networkFee": 0.00500000, // "engineSettings": { // "depositsEnabled": true, // "withdrawalsEnabled": true, // "displayEnabled": true, // "mobileAccessEnabled": true // }, // "minOrderValue": 0.00010000, // "maxOrderValue": 10000000000.00000000, // "maxMarketOrderValue": 10000000000.00000000, // "maxMarketOrderSize": 1000000000.00000000, // "digitalCurrencyType": "ETHEREUM", // "assetDivisibility": 0, // "assetIcon": "/images/currencies/crypto/ETH.svg" // }, // }, // "currencyPairs": { // "ETHUSD": { // "priceDecimals": 5, // "engineSettings": { // "tradingEnabled": true, // "displayEnabled": true, // "cancelOnly": true, // "verifyRequired": false, // "restrictedBuy": false, // "restrictedSell": false // }, // "minOrderRate": 10.00000000, // "maxOrderRate": 10000.00000000, // "displayPriceDecimals": 5, // "tradedCcy": "ETH", // "settlementCcy": "USD", // "preferredMarket": "ANX", // "chartEnabled": true, // "simpleTradeEnabled": false // }, // }, // }, // "timestamp": "1549840691039", // "resultCode": "OK" // } // const currencyStatic = this.safeValue (response, 'currencyStatic', {}); const currencies = this.safeValue (currencyStatic, 'currencies', {}); const result = {}; const ids = Object.keys (currencies); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const currency = currencies[id]; const code = this.safeCurrencyCode (id); const engineSettings = this.safeValue (currency, 'engineSettings'); const depositsEnabled = this.safeValue (engineSettings, 'depositsEnabled'); const withdrawalsEnabled = this.safeValue (engineSettings, 'withdrawalsEnabled'); const displayEnabled = this.safeValue (engineSettings, 'displayEnabled'); const active = depositsEnabled && withdrawalsEnabled && displayEnabled; const precision = this.safeInteger (currency, 'decimals'); const fee = this.safeFloat (currency, 'networkFee'); const type = this.safeStringLower (currency, 'type'); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': code, 'type': type, 'active': active, 'precision': precision, 'fee': fee, 'limits': { 'amount': { 'min': this.safeFloat (currency, 'minOrderSize'), 'max': this.safeFloat (currency, 'maxOrderSize'), }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeFloat (currency, 'minOrderValue'), 'max': this.safeFloat (currency, 'maxOrderValue'), }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, }; } return result; } async fetchMarkets (params = {}) { const response = await this.v3publicGetCurrencyStatic (params); // // { // "currencyStatic": { // "currencies": { // "HKD": { // "decimals": 2, // "minOrderSize": 1.00000000, // "maxOrderSize": 10000000000.00000000, // "displayDenominator": 1, // "summaryDecimals": 0, // "displayUnit": "HKD", // "symbol": "$", // "type": "FIAT", // "engineSettings": { // "depositsEnabled": false, // "withdrawalsEnabled": true, // "displayEnabled": true, // "mobileAccessEnabled": true // }, // "minOrderValue": 1.00000000, // "maxOrderValue": 10000000000.00000000, // "maxMarketOrderValue": 36000.00000000, // "maxMarketOrderSize": 36000.00000000, // "assetDivisibility": 0 // }, // "ETH": { // "decimals": 8, // "minOrderSize": 0.00010000, // "maxOrderSize": 1000000000.00000000, // "type": "CRYPTO", // "confirmationThresholds": [ // { "confosRequired": 30, "threshold": 0.50000000 }, // { "confosRequired": 45, "threshold": 10.00000000 }, // { "confosRequired": 70 } // ], // "networkFee": 0.00500000, // "engineSettings": { // "depositsEnabled": true, // "withdrawalsEnabled": true, // "displayEnabled": true, // "mobileAccessEnabled": true // }, // "minOrderValue": 0.00010000, // "maxOrderValue": 10000000000.00000000, // "maxMarketOrderValue": 10000000000.00000000, // "maxMarketOrderSize": 1000000000.00000000, // "digitalCurrencyType": "ETHEREUM", // "assetDivisibility": 0, // "assetIcon": "/images/currencies/crypto/ETH.svg" // }, // }, // "currencyPairs": { // "ETHUSD": { // "priceDecimals": 5, // "engineSettings": { // "tradingEnabled": true, // "displayEnabled": true, // "cancelOnly": true, // "verifyRequired": false, // "restrictedBuy": false, // "restrictedSell": false // }, // "minOrderRate": 10.00000000, // "maxOrderRate": 10000.00000000, // "displayPriceDecimals": 5, // "tradedCcy": "ETH", // "settlementCcy": "USD", // "preferredMarket": "ANX", // "chartEnabled": true, // "simpleTradeEnabled": false // }, // }, // }, // "timestamp": "1549840691039", // "resultCode": "OK" // } // const currencyStatic = this.safeValue (response, 'currencyStatic', {}); const currencies = this.safeValue (currencyStatic, 'currencies', {}); const currencyPairs = this.safeValue (currencyStatic, 'currencyPairs', {}); const result = []; const ids = Object.keys (currencyPairs); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = currencyPairs[id]; // // "ETHUSD": { // "priceDecimals": 5, // "engineSettings": { // "tradingEnabled": true, // "displayEnabled": true, // "cancelOnly": true, // "verifyRequired": false, // "restrictedBuy": false, // "restrictedSell": false // }, // "minOrderRate": 10.00000000, // "maxOrderRate": 10000.00000000, // "displayPriceDecimals": 5, // "tradedCcy": "ETH", // "settlementCcy": "USD", // "preferredMarket": "ANX", // "chartEnabled": true, // "simpleTradeEnabled": false // }, // const baseId = this.safeString (market, 'tradedCcy'); const quoteId = this.safeString (market, 'settlementCcy'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const baseCurrency = this.safeValue (currencies, baseId, {}); const quoteCurrency = this.safeValue (currencies, quoteId, {}); const precision = { 'price': this.safeInteger (market, 'priceDecimals'), 'amount': this.safeInteger (baseCurrency, 'decimals'), }; const engineSettings = this.safeValue (market, 'engineSettings'); const displayEnabled = this.safeValue (engineSettings, 'displayEnabled'); const tradingEnabled = this.safeValue (engineSettings, 'tradingEnabled'); const active = displayEnabled && tradingEnabled; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'active': active, 'limits': { 'price': { 'min': this.safeFloat (market, 'minOrderRate'), 'max': this.safeFloat (market, 'maxOrderRate'), }, 'amount': { 'min': this.safeFloat (baseCurrency, 'minOrderSize'), 'max': this.safeFloat (baseCurrency, 'maxOrderSize'), }, 'cost': { 'min': this.safeFloat (quoteCurrency, 'minOrderValue'), 'max': this.safeFloat (quoteCurrency, 'maxOrderValue'), }, }, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostMoneyInfo (params); const balance = this.safeValue (response, 'data', {}); const wallets = this.safeValue (balance, 'Wallets', {}); const currencyIds = Object.keys (wallets); const result = { 'info': balance }; for (let c = 0; c < currencyIds.length; c++) { const currencyId = currencyIds[c]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); const wallet = this.safeValue (wallets, currencyId); account['free'] = this.safeFloat (wallet['Available_Balance'], 'value'); account['total'] = this.safeFloat (wallet['Balance'], 'value'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'currency_pair': this.marketId (symbol), }; const response = await this.publicGetCurrencyPairMoneyDepthFull (this.extend (request, params)); const orderbook = this.safeValue (response, 'data', {}); const timestamp = this.safeIntegerProduct (orderbook, 'dataUpdateTime', 0.001); return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'currency_pair': this.marketId (symbol), }; const response = await this.publicGetCurrencyPairMoneyTicker (this.extend (request, params)); const ticker = this.safeValue (response, 'data', {}); const timestamp = this.safeIntegerProduct (ticker, 'dataUpdateTime', 0.001); const bid = this.safeFloat (ticker['buy'], 'value'); const ask = this.safeFloat (ticker['sell'], 'value'); const baseVolume = this.safeFloat (ticker['vol'], 'value'); const last = this.safeFloat (ticker['last'], 'value'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker['high'], 'value'), 'low': this.safeFloat (ticker['low'], 'value'), 'bid': bid, 'bidVolume': undefined, 'ask': ask, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker['avg'], 'value'), 'baseVolume': baseVolume, 'quoteVolume': undefined, 'info': ticker, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { throw new NotSupported (this.id + ' switched off the trades endpoint, see their docs at https://docs.anxv2.apiary.io'); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (limit !== undefined) { request['max'] = limit; } const response = await this.v3privatePostOrderList (this.extend (request, params)); const orders = this.safeValue (response, 'orders', []); const market = (symbol === undefined) ? undefined : this.market (symbol); return this.parseOrders (orders, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency_pair': market['id'], }; // ANXPro will return all symbol pairs regardless of what is specified in request const response = await this.privatePostCurrencyPairMoneyOrders (this.extend (request, params)); // // { // "result": "success", // "data": [ // { // "oid": "e74305c7-c424-4fbc-a8a2-b41d8329deb0", // "currency": "HKD", // "item": "BTC", // "type": "offer", // "amount": { // "currency": "BTC", // "display": "10.00000000 BTC", // "display_short": "10.00 BTC", // "value": "10.00000000", // "value_int": "1000000000" // }, // "effective_amount": { // "currency": "BTC", // "display": "10.00000000 BTC", // "display_short": "10.00 BTC", // "value": "10.00000000", // "value_int": "1000000000" // }, // "price": { // "currency": "HKD", // "display": "412.34567 HKD", // "display_short": "412.35 HKD", // "value": "412.34567", // "value_int": "41234567" // }, // "status": "open", // "date": 1393411075000, // "priority": 1393411075000000, // "actions": [] // }, // ... // ] // } // return this.parseOrders (this.safeValue (response, 'data', {}), market, since, limit); } parseOrder (order, market = undefined) { if ('orderId' in order) { return this.parseOrderV3 (order, market); } else { return this.parseOrderV2 (order, market); } } parseOrderStatus (status) { const statuses = { 'ACTIVE': 'open', 'FULL_FILL': 'closed', 'CANCEL': 'canceled', 'USER_CANCEL_PARTIAL': 'canceled', 'PARTIAL_FILL': 'canceled', }; return this.safeString (statuses, status, status); } parseOrderV3 (order, market = undefined) { // // v3 // // { // orderType: 'LIMIT', // tradedCurrency: 'XRP', // settlementCurrency: 'BTC', // tradedCurrencyAmount: '400.00000000', // buyTradedCurrency: true, // limitPriceInSettlementCurrency: '0.00007129', // timestamp: '1522547850000', // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', // tradedCurrencyAmountOutstanding: '0.00000000', // orderStatus: 'FULL_FILL', // executedAverageRate: '0.00007127', // trades: [ // { // tradeId: 'fe16b796-df57-41a2-b6d9-3489f189749e', // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', // timestamp: '1522547850000', // tradedCurrencyFillAmount: '107.91298639', // settlementCurrencyFillAmount: '0.00768772', // settlementCurrencyFillAmountUnrounded: '0.00768772', // price: '0.00007124', // ccyPair: 'XRPBTC' // }, // { // tradeId: 'e2962f67-c094-4243-8b88-0cdc70a1b1c7', // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', // timestamp: '1522547851000', // tradedCurrencyFillAmount: '292.08701361', // settlementCurrencyFillAmount: '0.02082288', // settlementCurrencyFillAmountUnrounded: '0.02082288', // price: '0.00007129', // ccyPair: 'XRPBTC' // } // ] // } // const status = this.parseOrderStatus (this.safeString (order, 'orderStatus')); const base = this.safeCurrencyCode (this.safeString (order, 'tradedCurrency')); const quote = this.safeCurrencyCode (this.safeString (order, 'settlementCurrency')); const symbol = base + '/' + quote; const buyTradedCurrency = this.safeString (order, 'buyTradedCurrency'); const side = (buyTradedCurrency === 'true') ? 'buy' : 'sell'; const timestamp = this.safeInteger (order, 'timestamp'); let lastTradeTimestamp = undefined; const trades = []; let filled = 0; const type = this.safeStringLower (order, 'orderType'); for (let i = 0; i < order['trades'].length; i++) { const trade = order['trades'][i]; const tradeTimestamp = this.safeInteger (trade, 'timestamp'); if (!lastTradeTimestamp || lastTradeTimestamp < tradeTimestamp) { lastTradeTimestamp = tradeTimestamp; } const parsedTrade = this.extend (this.parseTrade (trade), { 'side': side, 'type': type }); trades.push (parsedTrade); filled = this.sum (filled, parsedTrade['amount']); } const price = this.safeFloat (order, 'limitPriceInSettlementCurrency'); const executedAverageRate = this.safeFloat (order, 'executedAverageRate'); const remaining = (type === 'market') ? 0 : this.safeFloat (order, 'tradedCurrencyAmountOutstanding'); let amount = this.safeFloat (order, 'tradedCurrencyAmount'); if (!amount) { const settlementCurrencyAmount = this.safeFloat (order, 'settlementCurrencyAmount'); amount = settlementCurrencyAmount / executedAverageRate; } const cost = executedAverageRate * filled; return { 'id': this.safeString (order, 'orderId'), 'clientOrderId': undefined, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'remaining': remaining, 'filled': filled, 'status': status, 'fee': undefined, 'trades': trades, 'info': order, 'average': undefined, }; } parseOrderV2 (order, market = undefined) { // // v2 // // { // "oid": "e74305c7-c424-4fbc-a8a2-b41d8329deb0", // "currency": "HKD", // "item": "BTC", // "type": "offer", <-- bid/offer // "amount": { // "currency": "BTC", // "display": "10.00000000 BTC", // "display_short": "10.00 BTC", // "value": "10.00000000", // "value_int": "1000000000" // }, // "effective_amount": { // "currency": "BTC", // "display": "10.00000000 BTC", // "display_short": "10.00 BTC", // "value": "10.00000000", // "value_int": "1000000000" // }, // "price": { // "currency": "HKD", // "display": "412.34567 HKD", // "display_short": "412.35 HKD", // "value": "412.34567", // "value_int": "41234567" // }, // "status": "open", // "date": 1393411075000, // "priority": 1393411075000000, // "actions": [] // } // const id = this.safeString (order, 'oid'); const status = this.safeString (order, 'status'); const timestamp = this.safeInteger (order, 'date'); const baseId = this.safeString (order, 'item'); const quoteId = this.safeString (order, 'currency'); const marketId = baseId + '/' + quoteId; market = this.safeValue (this.markets_by_id, marketId); let symbol = undefined; if (typeof market !== 'undefined') { symbol = market['symbol']; } const amount_info = this.safeValue (order, 'amount', {}); const effective_info = this.safeValue (order, 'effective_amount', {}); const price_info = this.safeValue (order, 'price', {}); const remaining = this.safeFloat (effective_info, 'value'); const amount = this.safeFloat (amount_info, 'volume'); const price = this.safeFloat (price_info, 'value'); let filled = undefined; let cost = undefined; if (typeof amount !== 'undefined') { if (typeof remaining !== 'undefined') { filled = amount - remaining; cost = price * filled; } } const orderType = 'limit'; let side = this.safeString (order, 'type'); if (side === 'offer') { side = 'sell'; } else { side = 'buy'; } const fee = undefined; const trades = undefined; // todo parse trades const lastTradeTimestamp = undefined; return { 'info': order, 'id': id, 'clientOrderId': undefined, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'type': orderType, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'remaining': remaining, 'filled': filled, 'status': status, 'fee': fee, 'trades': trades, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const amountMultiplier = Math.pow (10, market['precision']['amount']); const request = { 'currency_pair': market['id'], 'amount_int': parseInt (amount * amountMultiplier), // 10^8 }; if (type === 'limit') { const priceMultiplier = Math.pow (10, market['precision']['price']); request['price_int'] = parseInt (price * priceMultiplier); // 10^5 or 10^8 } request['type'] = (side === 'buy') ? 'bid' : 'ask'; const response = await this.privatePostCurrencyPairMoneyOrderAdd (this.extend (request, params)); return { 'info': response, 'id': response['data'], }; } async cancelOrder (id, symbol = undefined, params = {}) { return await this.privatePostCurrencyPairMoneyOrderCancel ({ 'oid': id }); } getAmountMultiplier (code) { const multipliers = { 'BTC': 100000000, 'LTC': 100000000, 'STR': 100000000, 'XRP': 100000000, 'DOGE': 100000000, }; const defaultValue = 100; return this.safeInteger (multipliers, code, defaultValue); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const multiplier = this.getAmountMultiplier (code); const request = { 'currency': currency, 'amount_int': parseInt (amount * multiplier), 'address': address, }; if (tag !== undefined) { request['destinationTag'] = tag; } const response = await this.privatePostMoneyCurrencySendSimple (this.extend (request, params)); return { 'info': response, 'id': response['data']['transactionId'], }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privatePostMoneyCurrencyAddress (this.extend (request, params)); const data = this.safeValue (response, 'data', {}); const address = this.safeString (data, 'addr'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': undefined, 'info': response, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const request = this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'][api] + '/' + request; if (api === 'public' || api === 'v3public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); let auth = undefined; let contentType = undefined; if (api === 'v3private') { body = this.json (this.extend ({ 'tonce': nonce * 1000 }, query)); const path = url.replace ('https://anxpro.com/', ''); auth = path + '\0' + body; contentType = 'application/json'; } else { body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); // eslint-disable-next-line quotes auth = request + "\0" + body; contentType = 'application/x-www-form-urlencoded'; } const secret = this.base64ToBinary (this.secret); const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); headers = { 'Content-Type': contentType, 'Rest-Key': this.apiKey, 'Rest-Sign': this.decode (signature), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined || response === '') { return; } const result = this.safeString (response, 'result'); const code = this.safeString (response, 'resultCode'); const status = this.safeString (response, 'status'); if (((result !== undefined) && (result !== 'success')) || ((code !== undefined) && (code !== 'OK')) || (status !== undefined)) { const message = this.safeString (response, 'error'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], status, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],6:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ArgumentsRequired, ExchangeError, BadSymbol, InvalidOrder, PermissionDenied, InvalidAddress, AuthenticationError, InvalidNonce, BadRequest, InsufficientFunds, OrderNotFound } = require ('./base/errors'); const { TRUNCATE } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class aofex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'aofex', 'name': 'AOFEX', 'countries': [ 'GB' ], 'rateLimit': 1000, 'has': { 'fetchMarkets': true, 'fetchCurrencies': false, 'fetchOrderBook': true, 'fetchTrades': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchOHLCV': true, 'fetchBalance': true, 'createOrder': true, 'cancelOrder': true, 'cancelAllOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchClosedOrder': true, 'fetchOrderTrades': true, 'fetchTradingFee': true, }, 'timeframes': { '1m': '1min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '6h': '6hour', '12h': '12hour', '1d': '1day', '1w': '1week', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/77670271-056d1080-6f97-11ea-9ac2-4268e9ed0c1f.jpg', 'api': { 'public': 'https://openapi.aofex.com/openApi', 'private': 'https://openapi.aofex.com/openApi', }, 'www': 'https://aofex.com', 'doc': 'https://aofex.zendesk.com/hc/en-us/sections/360005576574-API', 'fees': 'https://aofex.zendesk.com/hc/en-us/articles/360025814934-Fees-on-AOFEX', 'referral': 'https://aofex.com/#/register?key=9763840', }, 'api': { 'public': { 'get': [ 'market/symbols', 'market/trade', 'market/depth', 'market/kline', 'market/precision', 'market/24kline', 'market/gears_depth', 'market/detail', ], }, 'private': { 'get': [ 'entrust/currentList', 'entrust/historyList', 'entrust/rate', 'wallet/list', 'entrust/detail', ], 'post': [ 'entrust/add', 'entrust/cancel', ], }, }, 'fees': { 'trading': { 'maker': 0.0019, 'taker': 0.002, }, }, 'exceptions': { 'exact': { '20001': ExchangeError, // request failure '20401': PermissionDenied, // no permission '20500': ExchangeError, // system error '20501': BadSymbol, // base symbol error '20502': ExchangeError, // base currency error '20503': ExchangeError, // base date error '20504': InsufficientFunds, // account frozen balance insufficient error '20505': BadRequest, // bad argument '20506': AuthenticationError, // api signature not valid '20507': ExchangeError, // gateway internal error '20508': InvalidAddress, // ad ethereum addresss '20509': InsufficientFunds, // order accountbalance error '20510': InvalidOrder, // order limitorder price error '20511': InvalidOrder, // order limitorder amount error '20512': InvalidOrder, // order orderprice precision error '20513': InvalidOrder, // order orderamount precision error '20514': InvalidOrder, // order marketorder amount error '20515': InvalidOrder, // order queryorder invalid '20516': InvalidOrder, // order orderstate error '20517': InvalidOrder, // order datelimit error '50518': InvalidOrder, // order update error '20519': InvalidNonce, // the nonce has been used '20520': InvalidNonce, // nonce expires, please verify server time '20521': BadRequest, // incomplete header parameters '20522': ExchangeError, // not getting the current user '20523': AuthenticationError, // please authenticate '20524': PermissionDenied, // btc account lockout '20525': AuthenticationError, // get API Key error '20526': PermissionDenied, // no query permission '20527': PermissionDenied, // no deal permission '20528': PermissionDenied, // no withdrawal permission '20529': AuthenticationError, // API Key expired '20530': PermissionDenied, // no permission }, 'broad': { }, }, 'options': { 'fetchBalance': { 'show_all': '0', // '1' to show zero balances }, }, }); } async fetchMarkets (params = {}) { let markets = await this.publicGetMarketSymbols (params); // // { // errno: 0, // errmsg: 'success', // result: [ // { // id: 2, // symbol: 'BTC-USDT', // base_currency: 'BTC', // quote_currency: 'USDT', // min_size: 0.00008, // max_size: 1300, // min_price: 1000, // max_price: 110000, // maker_fee: 1, // taker_fee: 1, // isHot: null, // isNew: null, // crown: null // }, // ] // } // let precisions = await this.publicGetMarketPrecision (); // // { // errno: 0, // errmsg: 'success', // result: { // 'MANA-USDT': { // amount: '2', // minQuantity: '32', // maxQuantity: '46000000', // price: '4', // minPrice: '0.003', // maxPrice: '0.35' // }, // } // } // precisions = this.safeValue (precisions, 'result', {}); markets = this.safeValue (markets, 'result', []); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'symbol'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const numericId = this.safeInteger (market, 'id'); const precision = this.safeValue (precisions, id, {}); const makerFee = this.safeFloat (market, 'maker_fee'); const takerFee = this.safeFloat (market, 'taker_fee'); result.push ({ 'id': id, 'numericId': numericId, 'symbol': symbol, 'baseId': baseId, 'quoteId': quoteId, 'base': base, 'quote': quote, 'active': undefined, 'maker': makerFee / 1000, 'taker': takerFee / 1000, 'precision': { 'amount': this.safeInteger (precision, 'amount'), 'price': this.safeInteger (precision, 'price'), }, 'limits': { 'amount': { 'min': this.safeFloat (market, 'min_size'), 'max': this.safeFloat (market, 'max_size'), }, 'price': { 'min': this.safeFloat (market, 'min_price'), 'max': this.safeFloat (market, 'max_price'), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }); } return result; } parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { // // { // id: 1584950100, // amount: "329.196", // count: 81, // open: "0.021155", // close: "0.021158", // low: "0.021144", // high: "0.021161", // vol: "6.963557767" // } // return [ this.safeTimestamp (ohlcv, 'id'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'amount'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 150; // default 150, max 2000 } const request = { 'symbol': market['id'], 'period': this.timeframes[timeframe], 'size': limit, // default 150, max 2000 }; const response = await this.publicGetMarketKline (this.extend (request, params)); // // { // errno: 0, // errmsg: "success", // result: { // ts: 1584950139003, // symbol: "ETH-BTC", // period: "1min", // data: [ // { // id: 1584950100, // amount: "329.196", // count: 81, // open: "0.021155", // close: "0.021158", // low: "0.021144", // high: "0.021161", // vol: "6.963557767" // }, // { // id: 1584950040, // amount: "513.265", // count: 151, // open: "0.021165", // close: "0.021155", // low: "0.021151", // high: "0.02118", // vol: "10.862806573" // }, // ] // } // } // const result = this.safeValue (response, 'result', {}); const data = this.safeValue (result, 'data', []); return this.parseOHLCVs (data, market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const options = this.safeValue (this.options, 'fetchBalance', {}); const showAll = this.safeValue (options, 'show_all', '0'); const request = { // 'currency': 'BTC', 'show_all': showAll, // required to show zero balances }; const response = await this.privateGetWalletList (this.extend (request, params)); // // { // "errno": 0, // "errmsg": "success", // "result": [ // { "available": "0", "frozen": "0", "currency": "BTC" } // ] // } // const result = { 'info': response }; const balances = this.safeValue (response, 'result', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'frozen'); result[code] = account; } return this.parseBalance (result); } async fetchTradingFee (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.privateGetEntrustRate (this.extend (request, params)); // // { // "errno":0, // "errmsg":"success", // "result": { // "toFee":"0.002","fromFee":"0.002" // } // } // const result = this.safeValue (response, 'result', {}); return { 'info': response, 'symbol': symbol, 'maker': this.safeFloat (result, 'fromFee'), 'taker': this.safeFloat (result, 'toFee'), }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; const response = await this.publicGetMarketDepth (this.extend (request, params)); // // { // errno: 0, // errmsg: "success", // result: { // buyType: 1, // sellType: 1, // ts: 1584950701050, // symbol: "ETH-BTC", // asks: [ // ["0.021227", "0.182"], // ["0.021249", "0.035"], // ["0.021253", "0.058"], // ], // bids: [ // ["0.021207", "0.039"], // ["0.021203", "0.051"], // ["0.02117", "2.326"], // ] // } // } // const result = this.safeValue (response, 'result', {}); const timestamp = this.safeInteger (result, 'ts'); return this.parseOrderBook (result, timestamp); } parseTicker (ticker, market = undefined) { // // fetchTicker // // { // id: 1584890087, // amount: '150032.919', // count: 134538, // open: '0.021394', // close: '0.021177', // low: '0.021053', // high: '0.021595', // vol: '3201.72451442' // } // const timestamp = this.safeTimestamp (ticker, 'id'); let symbol = undefined; if (market) { symbol = market['symbol']; } const open = this.safeFloat (ticker, 'open'); const last = this.safeFloat (ticker, 'close'); let change = undefined; if (symbol !== undefined) { change = parseFloat (this.priceToPrecision (symbol, last - open)); } else { change = last - open; } const average = this.sum (last, open) / 2; const percentage = change / open * 100; const baseVolume = this.safeFloat (ticker, 'amount'); const quoteVolume = this.safeFloat (ticker, 'vol'); let vwap = undefined; if (quoteVolume !== undefined) { if (baseVolume !== undefined) { if (baseVolume > 0) { vwap = parseFloat (this.priceToPrecision (symbol, quoteVolume / baseVolume)); } } } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': vwap, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbols !== undefined) { const ids = this.marketIds (symbols); request['symbol'] = ids.join (','); } const response = await this.publicGetMarket24kline (this.extend (request, params)); // // { // errno: 0, // errmsg: "success", // result: [ // { // symbol: "HB-AQ", // data: { // id: 1584893403, // amount: "4753751.243400354852648809", // count: 4724, // open: "6.3497", // close: "6.3318", // low: "6.011", // high: "6.5", // vol: "29538384.7873528796542891343493" // } // }, // ] // } // const tickers = this.safeValue (response, 'result', []); const result = {}; for (let i = 0; i < tickers.length; i++) { const marketId = this.safeString (tickers[i], 'symbol'); let market = undefined; let symbol = marketId; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } const data = this.safeValue (tickers[i], 'data', {}); const ticker = this.parseTicker (data, market); ticker['symbol'] = symbol; result[symbol] = ticker; } return this.filterByArray (result, 'symbol', symbols); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetMarketDetail (this.extend (request, params)); // // { // errno: 0, // errmsg: 'success', // result: { // id: 1584890087, // amount: '150032.919', // count: 134538, // open: '0.021394', // close: '0.021177', // low: '0.021053', // high: '0.021595', // vol: '3201.72451442' // } // } // const result = this.safeValue (response, 'result', {}); return this.parseTicker (result, market); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // id: 1584948803298490, // amount: "2.737", // price: "0.021209", // direction: "sell", // ts: 1584948803 // } // // fetchOrder trades // // { // "id":null, // "ctime":"2020-03-23 20:07:17", // "price":"123.9", // "number":"0.010688626311541565", // "total_price":"1.324320799999999903", // "fee":"0.000021377252623083" // } // const id = this.safeString (trade, 'id'); const ctime = this.parse8601 (this.safeString (trade, 'ctime')); const timestamp = this.safeTimestamp (trade, 'ts', ctime) - 28800000; // 8 hours, adjust to UTC; let symbol = undefined; if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const side = this.safeString (trade, 'direction'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat2 (trade, 'amount', 'number'); let cost = this.safeFloat (trade, 'total_price'); if ((cost === undefined) && (price !== undefined) && (amount !== undefined)) { cost = price * amount; } const feeCost = this.safeFloat (trade, 'fee'); let fee = undefined; if (feeCost !== undefined) { let feeCurrencyCode = undefined; if (market !== undefined) { if (side === 'buy') { feeCurrencyCode = market['base']; } else if (side === 'sell') { feeCurrencyCode = market['quote']; } } fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetMarketTrade (this.extend (request, params)); // // { // errno: 0, // errmsg: "success", // result: { // symbol: "ETH-BTC", // ts: 1584948805439, // data: [ // { // id: 1584948803300883, // amount: "0.583", // price: "0.021209", // direction: "buy", // ts: 1584948803 // }, // { // id: 1584948803298490, // amount: "2.737", // price: "0.021209", // direction: "sell", // ts: 1584948803 // }, // ] // } // } // const result = this.safeValue (response, 'result', {}); const data = this.safeValue (result, 'data', []); return this.parseTrades (data, market, since, limit); } parseOrderStatus (status) { const statuses = { '1': 'open', '2': 'open', // partially filled '3': 'closed', '4': 'canceled', // canceling '5': 'canceled', // partially canceled '6': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { order_sn: 'BM7442641584965237751ZMAKJ5' } // // fetchOpenOrders, fetchClosedOrders // // { // "order_sn": "BL74426415849672087836G48N1", // "symbol": "ETH-USDT", // "ctime": "2020-03-23 20:40:08", // "type": 2, // "side": "buy", // "price": "90", // undefined for market orders // "number": "0.1", // "total_price": "9.0", // 0 for market orders // "deal_number": null, // "deal_price": null, // "status": 1, // } // // fetchOrder // // { // order_sn: 'BM7442641584965237751ZMAKJ5', // symbol: 'ETH-USDT', // ctime: '2020-03-23 20:07:17', // type: 1, // side: 'buy', // price: '0', // number: '10', // total_price: '0', // deal_number: '0.080718626311541565', // deal_price: '123.890000000000000000', // status: 3, // // the trades field is injected by fetchOrder // trades: [ // { // id: null, // ctime: '2020-03-23 20:07:17', // price: '123.9', // number: '0.010688626311541565', // total_price: '1.324320799999999903', // fee: '0.000021377252623083' // } // ] // } // const id = this.safeString (order, 'order_sn'); const orderStatus = this.safeString (order, 'status'); const status = this.parseOrderStatus (orderStatus); let symbol = undefined; const marketId = this.safeString (order, 'symbol'); let base = undefined; let quote = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('-'); base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; base = market['base']; quote = market['quote']; } const timestamp = this.parse8601 (this.safeString (order, 'ctime')) - 28800000; // 8 hours, adjust to UTC const orderType = this.safeString (order, 'type'); const type = (orderType === '2') ? 'limit' : 'market'; const side = this.safeString (order, 'side'); // const amount = this.safeFloat (order, 'number'); // const price = this.safeFloat (order, 'price'); let cost = undefined; let price = undefined; let amount = undefined; let average = undefined; const number = this.safeFloat (order, 'number'); const totalPrice = this.safeFloat (order, 'total_price'); if (type === 'limit') { amount = number; price = this.safeFloat (order, 'price'); } else { average = this.safeFloat (order, 'deal_price'); if (side === 'buy') { amount = this.safeFloat (order, 'deal_number'); } else { amount = number; } } let fee = undefined; let trades = undefined; let filled = undefined; let feeCost = undefined; let remaining = undefined; let lastTradeTimestamp = undefined; // all orders except new orders and canceled orders if ((orderStatus !== '1') && (orderStatus !== '6')) { const rawTrades = this.safeValue (order, 'trades'); if (rawTrades !== undefined) { for (let i = 0; i < rawTrades.length; i++) { rawTrades[i]['direction'] = side; } trades = this.parseTrades (rawTrades, market, undefined, undefined, { 'symbol': market['symbol'], 'order': id, 'side': side, 'type': type, }); const tradesLength = trades.length; if (tradesLength > 0) { const firstTrade = trades[0]; feeCost = firstTrade['fee']['cost']; lastTradeTimestamp = firstTrade['timestamp']; filled = firstTrade['amount']; cost = firstTrade['cost']; for (let i = 1; i < trades.length; i++) { const trade = trades[i]; feeCost = this.sum (feeCost, trade['fee']['cost']); filled = this.sum (filled, trade['amount']); cost = this.sum (cost, trade['cost']); lastTradeTimestamp = Math.max (lastTradeTimestamp, trade['timestamp']); } if (amount !== undefined) { filled = Math.min (amount, filled); } if (filled > 0) { average = cost / filled; } } if (feeCost !== undefined) { const feeCurrencyCode = (side === 'buy') ? base : quote; fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } } } else { filled = 0; cost = 0; } if (cost === undefined) { if (type === 'limit') { cost = totalPrice; } else if (side === 'buy') { cost = number; } } if (filled === undefined) { if ((type === 'limit') && (orderStatus === '3')) { filled = amount; } } if (filled !== undefined) { if (amount !== undefined) { remaining = Math.max (amount - filled, 0); } } return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': trades, 'fee': fee, }; } async fetchClosedOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_sn': id, }; const response = await this.privateGetEntrustDetail (this.extend (request, params)); // // { // "errno": 0, // "errmsg": "success", // "result": { // "trades": [ // { // "id":null, // "ctime":"2020-03-23 20:07:17", // "price":"123.9", // "number":"0.010688626311541565", // "total_price":"1.324320799999999903", // "fee":"0.000021377252623083" // }, // ], // "entrust":{ // "order_sn":"BM7442641584965237751ZMAKJ5", // "symbol":"ETH-USDT", // "ctime":"2020-03-23 20:07:17", // "type":1, // "side":"buy", // "price":"0", // "number":"10", // "total_price":"0", // "deal_number":"0.080718626311541565", // "deal_price":"123.890000000000000000", // "status":3 // } // } // } // const result = this.safeValue (response, 'result', {}); const trades = this.safeValue (result, 'trades', []); const order = this.safeValue (result, 'entrust', {}); order['trades'] = trades; return this.parseOrder (order); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { const response = await this.fetchClosedOrder (id, symbol, params); return this.safeValue (response, 'trades', []); } async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'from': 'BM7442641584965237751ZMAKJ5', // query start order_sn 'direct': 'prev', // next }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; // default 20, max 100 } const response = await this[method] (this.extend (request, params)); // // { // "errno": 0, // "errmsg": "success", // "result": [ // { // "order_sn": "BL74426415849672087836G48N1", // "symbol": "ETH-USDT", // "ctime": "2020-03-23 20:40:08", // "type": 2, // "side": "buy", // "price": "90", // "number": "0.1", // "total_price": "9.0", // "deal_number": null, // "deal_price": null, // "status": 1, // } // ] // } // const result = this.safeValue (response, 'result', []); return this.parseOrders (result, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersWithMethod ('privateGetEntrustCurrentList', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersWithMethod ('privateGetEntrustHistoryList', symbol, since, limit, params); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const orderType = side + '-' + type; const request = { 'symbol': market['id'], 'type': orderType, }; if (type === 'limit') { request['amount'] = this.amountToPrecision (symbol, amount); request['price'] = this.priceToPrecision (symbol, price); } else if (type === 'market') { // for market buy it requires the amount of quote currency to spend if (side === 'buy') { const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true); let cost = amount; if (createMarketBuyOrderRequiresPrice) { if (price !== undefined) { cost = amount * price; } else { throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'amount' argument"); } } const precision = market['precision']['price']; request['amount'] = this.decimalToPrecision (cost, TRUNCATE, precision, this.precisionMode); } else { request['amount'] = this.amountToPrecision (symbol, amount); } } const response = await this.privatePostEntrustAdd (this.extend (request, params)); // // { // errno: 0, // errmsg: 'success', // result: { order_sn: 'BM7442641584965237751ZMAKJ5' } // } // const result = this.safeValue (response, 'result', {}); const order = this.parseOrder (result, market); const timestamp = this.milliseconds (); return this.extend (order, { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'amount': amount, 'price': price, 'type': type, 'side': side, }); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_ids': id, }; const response = await this.privatePostEntrustCancel (this.extend (request, params)); // // { // "errno": 0, // "errmsg": "success", // "result": { // "success": [ "avl12121", "bl3123123" ], // "failed": [ "sd24564", "sdf6564564" ] // } // } // const result = this.safeValue (response, 'result', {}); const success = this.safeValue (result, 'success', []); if (!this.inArray (id, success)) { throw new OrderNotFound (this.id + ' order id ' + id + ' not found in successfully canceled orders: ' + this.json (response)); } const timestamp = undefined; return { 'info': response, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': 'canceled', 'symbol': symbol, 'type': undefined, 'side': undefined, 'price': undefined, 'cost': undefined, 'average': undefined, 'amount': undefined, 'filled': undefined, 'remaining': undefined, 'trades': undefined, 'fee': undefined, 'clientOrderId': undefined, }; } async cancelAllOrders (symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelAllOrders() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.privatePostEntrustCancel (this.extend (request, params)); // // { // "errno": 0, // "errmsg": "success", // "result": { // "success": [ "avl12121", "bl3123123" ], // "failed": [ "sd24564", "sdf6564564" ] // } // } // return response; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + path; let keys = Object.keys (params); const keysLength = keys.length; if (api === 'public') { if (keysLength > 0) { url += '?' + this.urlencode (params); } } else { const nonce = this.nonce ().toString (); const uuid = this.uuid (); const randomString = uuid.slice (0, 5); const nonceString = nonce + '_' + randomString; const auth = {}; auth[this.apiKey] = this.apiKey; auth[this.secret] = this.secret; auth[nonceString] = nonceString; for (let i = 0; i < keysLength; i++) { const key = keys[i]; auth[key] = key + '=' + params[key]; } const keysorted = this.keysort (auth); let stringToSign = ''; keys = Object.keys (keysorted); for (let i = 0; i < keys.length; i++) { const key = keys[i]; stringToSign += keysorted[key]; } const signature = this.hash (this.encode (stringToSign), 'sha1'); headers = { 'Nonce': nonceString, 'Token': this.apiKey, 'Signature': signature, }; if (method === 'POST') { headers['Content-Type'] = 'application/x-www-form-urlencoded'; if (keysLength > 0) { body = this.urlencode (params); } } else { if (keysLength > 0) { url += '?' + this.urlencode (params); } } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } // // {"errno":20501,"errmsg":"base symbol error"} // const error = this.safeString (response, 'errno'); if ((error !== undefined) && (error !== '0')) { const message = this.safeString (response, 'errmsg'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],7:[function(require,module,exports){ (function (Buffer){ "use strict"; // ---------------------------------------------------------------------------- const functions = require ('./functions') const { isNode , keys , values , deepExtend , extend , clone , flatten , unique , indexBy , sortBy , groupBy , aggregate , uuid , unCamelCase , precisionFromString , throttle , capitalize , now , timeout , TimedOut , buildOHLCVC , decimalToPrecision , defaultFetch } = functions const { ExchangeError , BadSymbol , InvalidAddress , NotSupported , AuthenticationError , DDoSProtection , RequestTimeout , ExchangeNotAvailable , RateLimitExceeded } = require ('./errors') const { TRUNCATE, ROUND, DECIMAL_PLACES } = functions.precisionConstants const BN = require ('../static_dependencies/BN/bn') // ---------------------------------------------------------------------------- // web3 / 0x imports let ethAbi = undefined , ethUtil = undefined try { const requireFunction = require; ethAbi = requireFunction ('ethereumjs-abi') // eslint-disable-line global-require ethUtil = requireFunction ('ethereumjs-util') // eslint-disable-line global-require // we prefer bignumber.js over BN.js // BN = requireFunction ('bn.js') // eslint-disable-line global-require } catch (e) { // nothing } // ---------------------------------------------------------------------------- module.exports = class Exchange { describe () { return { 'id': undefined, 'name': undefined, 'countries': undefined, 'enableRateLimit': false, 'rateLimit': 2000, // milliseconds = seconds * 1000 'certified': false, 'pro': false, 'has': { 'cancelAllOrders': false, 'cancelOrder': true, 'cancelOrders': false, 'CORS': false, 'createDepositAddress': false, 'createLimitOrder': true, 'createMarketOrder': true, 'createOrder': true, 'deposit': false, 'editOrder': 'emulated', 'fetchBalance': true, 'fetchBidsAsks': false, 'fetchClosedOrders': false, 'fetchCurrencies': false, 'fetchDepositAddress': false, 'fetchDeposits': false, 'fetchFundingFees': false, 'fetchL2OrderBook': true, 'fetchLedger': false, 'fetchMarkets': true, 'fetchMyTrades': false, 'fetchOHLCV': 'emulated', 'fetchOpenOrders': false, 'fetchOrder': false, 'fetchOrderBook': true, 'fetchOrderBooks': false, 'fetchOrders': false, 'fetchOrderTrades': false, 'fetchStatus': 'emulated', 'fetchTicker': true, 'fetchTickers': false, 'fetchTime': false, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTradingLimits': false, 'fetchTransactions': false, 'fetchWithdrawals': false, 'privateAPI': true, 'publicAPI': true, 'withdraw': false, }, 'urls': { 'logo': undefined, 'api': undefined, 'www': undefined, 'doc': undefined, 'fees': undefined, }, 'api': undefined, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': false, 'login': false, 'password': false, 'twofa': false, // 2-factor authentication (one-time password key) 'privateKey': false, // a "0x"-prefixed hexstring private key for a wallet 'walletAddress': false, // the wallet address "0x"-prefixed hexstring 'token': false, // reserved for HTTP auth in some cases }, 'markets': undefined, // to be filled manually or by fetchMarkets 'currencies': {}, // to be filled manually or by fetchMarkets 'timeframes': undefined, // redefine if the exchange has.fetchOHLCV 'fees': { 'trading': { 'tierBased': undefined, 'percentage': undefined, 'taker': undefined, 'maker': undefined, }, 'funding': { 'tierBased': undefined, 'percentage': undefined, 'withdraw': {}, 'deposit': {}, }, }, 'status': { 'status': 'ok', 'updated': undefined, 'eta': undefined, 'url': undefined, }, 'exceptions': undefined, 'httpExceptions': { '422': ExchangeError, '418': DDoSProtection, '429': RateLimitExceeded, '404': ExchangeNotAvailable, '409': ExchangeNotAvailable, '410': ExchangeNotAvailable, '500': ExchangeNotAvailable, '501': ExchangeNotAvailable, '502': ExchangeNotAvailable, '520': ExchangeNotAvailable, '521': ExchangeNotAvailable, '522': ExchangeNotAvailable, '525': ExchangeNotAvailable, '526': ExchangeNotAvailable, '400': ExchangeNotAvailable, '403': ExchangeNotAvailable, '405': ExchangeNotAvailable, '503': ExchangeNotAvailable, '530': ExchangeNotAvailable, '408': RequestTimeout, '504': RequestTimeout, '401': AuthenticationError, '511': AuthenticationError, }, // some exchanges report only 'free' on `fetchBlance` call (i.e. report no 'used' funds) // in this case ccxt will try to infer 'used' funds from open order cache, which might be stale // still, some exchanges report number of open orders together with balance // if you set the following flag to 'true' ccxt will leave 'used' funds undefined in case of discrepancy 'dontGetUsedBalanceFromStaleCache': false, 'commonCurrencies': { // gets extended/overwritten in subclasses 'XBT': 'BTC', 'BCC': 'BCH', 'DRK': 'DASH', 'BCHABC': 'BCH', 'BCHSV': 'BSV', }, 'precisionMode': DECIMAL_PLACES, 'limits': { 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, }, } // return } // describe () constructor (userConfig = {}) { Object.assign (this, functions) // if (isNode) { // this.nodeVersion = process.version.match (/\d+\.\d+\.\d+/)[0] // this.userAgent = { // 'User-Agent': 'ccxt/' + Exchange.ccxtVersion + // ' (+https://github.com/ccxt/ccxt)' + // ' Node.js/' + this.nodeVersion + ' (JavaScript)' // } // } this.options = {} // exchange-specific options, if any // fetch implementation options (JS only) this.fetchOptions = { // keepalive: true, // does not work in Chrome, https://github.com/ccxt/ccxt/issues/6368 } this.userAgents = { 'chrome': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 'chrome39': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36', } this.headers = {} // prepended to URL, like https://proxy.com/https://exchange.com/api... this.proxy = '' this.origin = '*' // CORS origin this.minFundingAddressLength = 1 // used in checkAddress this.substituteCommonCurrencyCodes = true // reserved // do not delete this line, it is needed for users to be able to define their own fetchImplementation this.fetchImplementation = defaultFetch this.timeout = 10000 // milliseconds this.verbose = false this.debug = false this.userAgent = undefined this.twofa = undefined // two-factor authentication (2FA) this.apiKey = undefined this.secret = undefined this.uid = undefined this.login = undefined this.password = undefined this.privateKey = undefined // a "0x"-prefixed hexstring private key for a wallet this.walletAddress = undefined // a wallet address "0x"-prefixed hexstring this.token = undefined // reserved for HTTP auth in some cases this.balance = {} this.orderbooks = {} this.tickers = {} this.orders = {} this.trades = {} this.transactions = {} this.ohlcvs = {} this.requiresWeb3 = false this.precision = {} this.enableLastJsonResponse = true this.enableLastHttpResponse = true this.enableLastResponseHeaders = true this.last_http_response = undefined this.last_json_response = undefined this.last_response_headers = undefined const unCamelCaseProperties = (obj = this) => { if (obj !== null) { for (const k of Object.getOwnPropertyNames (obj)) { this[unCamelCase (k)] = this[k] } unCamelCaseProperties (Object.getPrototypeOf (obj)) } } unCamelCaseProperties () // merge configs const config = deepExtend (this.describe (), userConfig) // merge to this for (const [property, value] of Object.entries (config)) { this[property] = deepExtend (this[property], value) } if (!this.httpAgent) { this.httpAgent = defaultFetch.http ? new defaultFetch.http.Agent ({ 'keepAlive': true }) : undefined } if (!this.httpsAgent) { this.httpsAgent = defaultFetch.https ? new defaultFetch.https.Agent ({ 'keepAlive': true }) : undefined } // generate old metainfo interface for (const k in this.has) { this['has' + capitalize (k)] = !!this.has[k] // converts 'emulated' to true } if (this.api) { this.defineRestApi (this.api, 'request') } this.initRestRateLimiter () if (this.markets) { this.setMarkets (this.markets) } } defaults () { return { /* override me */ } } nonce () { return this.seconds () } encodeURIComponent (...args) { return encodeURIComponent (...args) } checkRequiredCredentials (error = true) { const keys = Object.keys (this.requiredCredentials) for (let i = 0; i < keys.length; i++) { const key = keys[i] if (this.requiredCredentials[key] && !this[key]) { if (error) { throw new AuthenticationError (this.id + ' requires `' + key + '` credential') } else { return error } } } return true } checkAddress (address) { if (address === undefined) { throw new InvalidAddress (this.id + ' address is undefined') } // check the address is not the same letter like 'aaaaa' nor too short nor has a space if ((unique (address).length === 1) || address.length < this.minFundingAddressLength || address.includes (' ')) { throw new InvalidAddress (this.id + ' address is invalid or has less than ' + this.minFundingAddressLength.toString () + ' characters: "' + this.json (address) + '"') } return address } initRestRateLimiter () { if (this.rateLimit === undefined) { throw new Error (this.id + '.rateLimit property is not configured') } this.tokenBucket = this.extend ({ delay: 1, capacity: 1, defaultCost: 1, maxCapacity: 1000, }, this.tokenBucket) this.throttle = throttle (this.tokenBucket) this.executeRestRequest = (url, method = 'GET', headers = undefined, body = undefined) => { // fetchImplementation cannot be called on this. in browsers: // TypeError Failed to execute 'fetch' on 'Window': Illegal invocation const fetchImplementation = this.fetchImplementation const params = { method, headers, body, timeout: this.timeout } if (this.agent) { this.agent.keepAlive = true params['agent'] = this.agent } else if (this.httpAgent && url.indexOf ('http://') === 0) { params['agent'] = this.httpAgent } else if (this.httpsAgent && url.indexOf ('https://') === 0) { params['agent'] = this.httpsAgent } const promise = fetchImplementation (url, this.extend (params, this.fetchOptions)) .catch ((e) => { if (isNode) { throw new ExchangeNotAvailable ([ this.id, method, url, e.type, e.message ].join (' ')) } throw e // rethrow all unknown errors }) .then ((response) => this.handleRestResponse (response, url, method, headers, body)) return timeout (this.timeout, promise).catch ((e) => { if (e instanceof TimedOut) { throw new RequestTimeout (this.id + ' ' + method + ' ' + url + ' request timed out (' + this.timeout + ' ms)') } throw e }) } } setSandboxMode (enabled) { if (!!enabled) { // eslint-disable-line no-extra-boolean-cast if ('test' in this.urls) { if (typeof this.urls['api'] === 'string') { this.urls['api_backup'] = this.urls['api'] this.urls['api'] = this.urls['test'] } else { this.urls['api_backup'] = clone (this.urls['api']) this.urls['api'] = clone (this.urls['test']) } } else { throw new NotSupported (this.id + ' does not have a sandbox URL') } } else if ('api_backup' in this.urls) { if (typeof this.urls['api'] === 'string') { this.urls['api'] = this.urls['api_backup'] } else { this.urls['api'] = clone (this.urls['api_backup']) } } } defineRestApi (api, methodName, options = {}) { for (const type of Object.keys (api)) { for (const httpMethod of Object.keys (api[type])) { let paths = api[type][httpMethod] for (let i = 0; i < paths.length; i++) { let path = paths[i].trim () let splitPath = path.split (/[^a-zA-Z0-9]/) let uppercaseMethod = httpMethod.toUpperCase () let lowercaseMethod = httpMethod.toLowerCase () let camelcaseMethod = this.capitalize (lowercaseMethod) let camelcaseSuffix = splitPath.map (this.capitalize).join ('') let underscoreSuffix = splitPath.map (x => x.trim ().toLowerCase ()).filter (x => x.length > 0).join ('_') let camelcase = type + camelcaseMethod + this.capitalize (camelcaseSuffix) let underscore = type + '_' + lowercaseMethod + '_' + underscoreSuffix if ('suffixes' in options) { if ('camelcase' in options['suffixes']) { camelcase += options['suffixes']['camelcase'] } if ('underscore' in options.suffixes) { underscore += options['suffixes']['underscore'] } } if ('underscore_suffix' in options) { underscore += options.underscoreSuffix; } if ('camelcase_suffix' in options) { camelcase += options.camelcaseSuffix; } let partial = async params => this[methodName] (path, type, uppercaseMethod, params || {}) this[camelcase] = partial this[underscore] = partial } } } } print (... args) { console.log (... args) } fetch (url, method = 'GET', headers = undefined, body = undefined) { if (isNode && this.userAgent) { if (typeof this.userAgent === 'string') { headers = extend ({ 'User-Agent': this.userAgent }, headers) } else if ((typeof this.userAgent === 'object') && ('User-Agent' in this.userAgent)) { headers = extend (this.userAgent, headers) } } if (typeof this.proxy === 'function') { url = this.proxy (url) if (isNode) { headers = extend ({ 'Origin': this.origin }, headers) } } else if (typeof this.proxy === 'string') { if (this.proxy.length && isNode) { headers = extend ({ 'Origin': this.origin }, headers) } url = this.proxy + url } headers = extend (this.headers, headers) if (this.verbose) { this.print ("fetch:\n", this.id, method, url, "\nRequest:\n", headers, "\n", body, "\n") } return this.executeRestRequest (url, method, headers, body) } async fetch2 (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { if (this.enableRateLimit) { await this.throttle (this.rateLimit) } const request = this.sign (path, type, method, params, headers, body) return this.fetch (request.url, request.method, request.headers, request.body) } request (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { return this.fetch2 (path, type, method, params, headers, body) } parseJson (jsonString) { try { if (this.isJsonEncodedObject (jsonString)) { return JSON.parse (jsonString) } } catch (e) { // SyntaxError return undefined } } throwExactlyMatchedException (exact, string, message) { if (string in exact) { throw new exact[string] (message) } } throwBroadlyMatchedException (broad, string, message) { const broadKey = this.findBroadlyMatchedKey (broad, string) if (broadKey !== undefined) { throw new broad[broadKey] (message) } } // a helper for matching error strings exactly vs broadly findBroadlyMatchedKey (broad, string) { const keys = Object.keys (broad) for (let i = 0; i < keys.length; i++) { const key = keys[i] if (string.indexOf (key) >= 0) { return key } } return undefined } handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { // override me } defaultErrorHandler (code, reason, url, method, headers, body, response) { if ((code >= 200) && (code <= 299)) { return } let details = body const codeAsString = code.toString () let ErrorClass = undefined if (codeAsString in this.httpExceptions) { ErrorClass = this.httpExceptions[codeAsString] } if (response === undefined) { const maintenance = body.match (/offline|busy|retry|wait|unavailable|maintain|maintenance|maintenancing/i) const ddosProtection = body.match (/cloudflare|incapsula|overload|ddos/i) if (maintenance) { ErrorClass = ExchangeNotAvailable details += ' offline, on maintenance, or unreachable from this location at the moment' } if (ddosProtection) { ErrorClass = DDoSProtection } } if (ErrorClass === ExchangeNotAvailable) { details += ' (possible reasons: ' + [ 'invalid API keys', 'bad or old nonce', 'exchange is down or offline', 'on maintenance', 'DDoS protection', 'rate-limiting', ].join (', ') + ')' } if (ErrorClass !== undefined) { throw new ErrorClass ([ this.id, method, url, code, reason, details ].join (' ')) } } getResponseHeaders (response) { const result = {} response.headers.forEach ((value, key) => { key = key.split ('-').map ((word) => capitalize (word)).join ('-') result[key] = value }) return result } handleRestResponse (response, url, method = 'GET', requestHeaders = undefined, requestBody = undefined) { return response.text ().then ((responseBody) => { const json = this.parseJson (responseBody) const responseHeaders = this.getResponseHeaders (response) if (this.enableLastResponseHeaders) { this.last_response_headers = responseHeaders } if (this.enableLastHttpResponse) { this.last_http_response = responseBody // FIXME: for those classes that haven't switched to handleErrors yet } if (this.enableLastJsonResponse) { this.last_json_response = json // FIXME: for those classes that haven't switched to handleErrors yet } if (this.verbose) { this.print ("handleRestResponse:\n", this.id, method, url, response.status, response.statusText, "\nResponse:\n", responseHeaders, "\n", responseBody, "\n") } this.handleErrors (response.status, response.statusText, url, method, responseHeaders, responseBody, json, requestHeaders, requestBody) this.defaultErrorHandler (response.status, response.statusText, url, method, responseHeaders, responseBody, json) return json || responseBody }) } setMarkets (markets, currencies = undefined) { const values = Object.values (markets).map ((market) => deepExtend ({ 'limits': this.limits, 'precision': this.precision, }, this.fees['trading'], market)) this.markets = indexBy (values, 'symbol') this.marketsById = indexBy (markets, 'id') this.markets_by_id = this.marketsById this.symbols = Object.keys (this.markets).sort () this.ids = Object.keys (this.markets_by_id).sort () if (currencies) { this.currencies = deepExtend (currencies, this.currencies) } else { let baseCurrencies = values.filter (market => 'base' in market) .map (market => ({ id: market.baseId || market.base, numericId: (market.baseNumericId !== undefined) ? market.baseNumericId : undefined, code: market.base, precision: market.precision ? (market.precision.base || market.precision.amount) : 8, })) let quoteCurrencies = values.filter (market => 'quote' in market) .map (market => ({ id: market.quoteId || market.quote, numericId: (market.quoteNumericId !== undefined) ? market.quoteNumericId : undefined, code: market.quote, precision: market.precision ? (market.precision.quote || market.precision.price) : 8, })) baseCurrencies = sortBy (baseCurrencies, 'code') quoteCurrencies = sortBy (quoteCurrencies, 'code') this.baseCurrencies = indexBy (baseCurrencies, 'code') this.quoteCurrencies = indexBy (quoteCurrencies, 'code') const allCurrencies = baseCurrencies.concat (quoteCurrencies) const groupedCurrencies = groupBy (allCurrencies, 'code') const currencies = Object.keys (groupedCurrencies).map (code => groupedCurrencies[code].reduce ((previous, current) => ((previous.precision > current.precision) ? previous : current), groupedCurrencies[code][0])) const sortedCurrencies = sortBy (flatten (currencies), 'code') this.currencies = deepExtend (indexBy (sortedCurrencies, 'code'), this.currencies) } this.currencies_by_id = indexBy (this.currencies, 'id') return this.markets } async loadMarketsHelper (reload = false, params = {}) { if (!reload && this.markets) { if (!this.markets_by_id) { return this.setMarkets (this.markets) } return this.markets } let currencies = undefined if (this.has.fetchCurrencies) { currencies = await this.fetchCurrencies () } const markets = await this.fetchMarkets (params) return this.setMarkets (markets, currencies) } // is async (returns a promise) loadMarkets (reload = false, params = {}) { if ((reload && !this.reloadingMarkets) || !this.marketsLoading) { this.reloadingMarkets = true this.marketsLoading = this.loadMarketsHelper (reload, params).then ((resolved) => { this.reloadingMarkets = false return resolved }, (error) => { this.reloadingMarkets = false throw error }) } return this.marketsLoading } async loadAccounts (reload = false, params = {}) { if (reload) { this.accounts = await this.fetchAccounts (params) } else { if (this.accounts) { return this.accounts } else { this.accounts = await this.fetchAccounts (params) } } this.accountsById = this.indexBy (this.accounts, 'id') return this.accounts } fetchBidsAsks (symbols = undefined, params = {}) { throw new NotSupported (this.id + ' fetchBidsAsks not supported yet') } async fetchOHLCVC (symbol, timeframe = '1m', since = undefined, limits = undefined, params = {}) { if (!this.has['fetchTrades']) throw new NotSupported (this.id + ' fetchOHLCV() not supported yet') await this.loadMarkets () const trades = await this.fetchTrades (symbol, since, limits, params) const ohlcvc = buildOHLCVC (trades, timeframe, since, limits) return ohlcvc } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limits = undefined, params = {}) { if (!this.has['fetchTrades']) throw new NotSupported (this.id + ' fetchOHLCV() not supported yet') await this.loadMarkets () const trades = await this.fetchTrades (symbol, since, limits, params) const ohlcvc = buildOHLCVC (trades, timeframe, since, limits) return ohlcvc.map (c => c.slice (0, -1)) } parseTradingViewOHLCV (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { const result = this.convertTradingViewToOHLCV (ohlcvs) return this.parseOHLCVs (result, market, timeframe, since, limit) } convertTradingViewToOHLCV (ohlcvs, t = 't', o = 'o', h = 'h', l = 'l', c = 'c', v = 'v', ms = false) { const result = []; for (let i = 0; i < ohlcvs[t].length; i++) { result.push ([ ms ? ohlcvs[t][i] : (ohlcvs[t][i] * 1000), ohlcvs[o][i], ohlcvs[h][i], ohlcvs[l][i], ohlcvs[c][i], ohlcvs[v][i], ]) } return result } convertOHLCVToTradingView (ohlcvs, t = 't', o = 'o', h = 'h', l = 'l', c = 'c', v = 'v', ms = false) { const result = {} result[t] = [] result[o] = [] result[h] = [] result[l] = [] result[c] = [] result[v] = [] for (let i = 0; i < ohlcvs.length; i++) { result[t].push (ms ? ohlcvs[i][0] : parseInt (ohlcvs[i][0] / 1000)) result[o].push (ohlcvs[i][1]) result[h].push (ohlcvs[i][2]) result[l].push (ohlcvs[i][3]) result[c].push (ohlcvs[i][4]) result[v].push (ohlcvs[i][5]) } return result } fetchTicker (symbol, params = {}) { throw new NotSupported (this.id + ' fetchTicker not supported yet') } fetchTickers (symbols = undefined, params = {}) { throw new NotSupported (this.id + ' fetchTickers not supported yet') } purgeCachedOrders (before) { const orders = Object .values (this.orders) .filter (order => (order.status === 'open') || (order.timestamp >= before)) this.orders = indexBy (orders, 'id') return this.orders } fetchOrder (id, symbol = undefined, params = {}) { throw new NotSupported (this.id + ' fetchOrder not supported yet'); } fetchUnifiedOrder (order, params = {}) { return this.fetchOrder (this.safeValue (order, 'id'), this.safeValue (order, 'symbol'), params); } createOrder (symbol, type, side, amount, price = undefined, params = {}) { throw new NotSupported (this.id + ' createOrder not supported yet'); } cancelOrder (id, symbol = undefined, params = {}) { throw new NotSupported (this.id + ' cancelOrder not supported yet'); } cancelUnifiedOrder (order, params = {}) { return this.cancelOrder (this.safeValue (order, 'id'), this.safeValue (order, 'symbol'), params); } fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { throw new NotSupported (this.id + ' fetchOrders not supported yet'); } fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { throw new NotSupported (this.id + ' fetchOpenOrders not supported yet'); } fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { throw new NotSupported (this.id + ' fetchClosedOrders not supported yet'); } fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { throw new NotSupported (this.id + ' fetchMyTrades not supported yet'); } fetchTransactions (symbol = undefined, since = undefined, limit = undefined, params = {}) { throw new NotSupported (this.id + ' fetchTransactions not supported yet'); } fetchDeposits (symbol = undefined, since = undefined, limit = undefined, params = {}) { throw new NotSupported (this.id + ' fetchDeposits not supported yet'); } fetchWithdrawals (symbol = undefined, since = undefined, limit = undefined, params = {}) { throw new NotSupported (this.id + ' fetchWithdrawals not supported yet'); } fetchCurrencies (params = {}) { // markets are returned as a list // currencies are returned as a dict // this is for historical reasons // and may be changed for consistency later return new Promise ((resolve, reject) => resolve (this.currencies)); } fetchMarkets (params = {}) { // markets are returned as a list // currencies are returned as a dict // this is for historical reasons // and may be changed for consistency later return new Promise ((resolve, reject) => resolve (Object.values (this.markets))) } async fetchOrderStatus (id, symbol = undefined, params = {}) { const order = await this.fetchOrder (id, symbol, params); return order['status']; } account () { return { 'free': undefined, 'used': undefined, 'total': undefined, } } commonCurrencyCode (currency) { if (!this.substituteCommonCurrencyCodes) return currency return this.safeString (this.commonCurrencies, currency, currency) } currencyId (commonCode) { if (this.currencies === undefined) { throw new ExchangeError (this.id + ' currencies not loaded') } if (commonCode in this.currencies) { return this.currencies[commonCode]['id']; } const currencyIds = {} const distinct = Object.keys (this.commonCurrencies) for (let i = 0; i < distinct.length; i++) { const k = distinct[i] currencyIds[this.commonCurrencies[k]] = k } return this.safeString (currencyIds, commonCode, commonCode) } currency (code) { if (this.currencies === undefined) throw new ExchangeError (this.id + ' currencies not loaded') if ((typeof code === 'string') && (code in this.currencies)) return this.currencies[code] throw new ExchangeError (this.id + ' does not have currency code ' + code) } market (symbol) { if (this.markets === undefined) throw new ExchangeError (this.id + ' markets not loaded') if ((typeof symbol === 'string') && (symbol in this.markets)) return this.markets[symbol] throw new BadSymbol (this.id + ' does not have market symbol ' + symbol) } marketId (symbol) { const market = this.market (symbol) return (market !== undefined ? market['id'] : symbol) } marketIds (symbols) { return symbols.map (symbol => this.marketId (symbol)); } symbol (symbol) { return this.market (symbol).symbol || symbol } url (path, params = {}) { let result = this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)) if (Object.keys (query).length) result += '?' + this.urlencode (query) return result } parseBidAsk (bidask, priceKey = 0, amountKey = 1) { const price = parseFloat (bidask[priceKey]) const amount = parseFloat (bidask[amountKey]) return [ price, amount ] } parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) { return Object.values (bidasks || []).map (bidask => this.parseBidAsk (bidask, priceKey, amountKey)) } async fetchL2OrderBook (symbol, limit = undefined, params = {}) { const orderbook = await this.fetchOrderBook (symbol, limit, params) return extend (orderbook, { 'bids': sortBy (aggregate (orderbook.bids), 0, true), 'asks': sortBy (aggregate (orderbook.asks), 0), }) } parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1) { return { 'bids': sortBy ((bidsKey in orderbook) ? this.parseBidsAsks (orderbook[bidsKey], priceKey, amountKey) : [], 0, true), 'asks': sortBy ((asksKey in orderbook) ? this.parseBidsAsks (orderbook[asksKey], priceKey, amountKey) : [], 0), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'nonce': undefined, } } parseBalance (balance) { const currencies = Object.keys (this.omit (balance, [ 'info', 'free', 'used', 'total' ])); balance['free'] = {} balance['used'] = {} balance['total'] = {} for (const currency of currencies) { if (balance[currency].total === undefined) { if (balance[currency].free !== undefined && balance[currency].used !== undefined) { balance[currency].total = this.sum (balance[currency].free, balance[currency].used) } } if (balance[currency].free === undefined) { if (balance[currency].total !== undefined && balance[currency].used !== undefined) { balance[currency].free = this.sum (balance[currency].total, -balance[currency].used) } } if (balance[currency].used === undefined) { if (balance[currency].total !== undefined && balance[currency].free !== undefined) { balance[currency].used = this.sum (balance[currency].total, -balance[currency].free) } } balance.free[currency] = balance[currency].free balance.used[currency] = balance[currency].used balance.total[currency] = balance[currency].total } return balance } async fetchPartialBalance (part, params = {}) { const balance = await this.fetchBalance (params) return balance[part] } fetchFreeBalance (params = {}) { return this.fetchPartialBalance ('free', params) } fetchUsedBalance (params = {}) { return this.fetchPartialBalance ('used', params) } fetchTotalBalance (params = {}) { return this.fetchPartialBalance ('total', params) } async fetchStatus (params = {}) { if (this.has['fetchTime']) { const time = await this.fetchTime (params) this.status = this.extend (this.status, { 'updated': time, }) } return this.status } async fetchTradingFees (params = {}) { throw new NotSupported (this.id + ' fetchTradingFees not supported yet') } async fetchTradingFee (symbol, params = {}) { if (!this.has['fetchTradingFees']) { throw new NotSupported (this.id + ' fetchTradingFee not supported yet') } return await this.fetchTradingFees (params) } async loadTradingLimits (symbols = undefined, reload = false, params = {}) { if (this.has['fetchTradingLimits']) { if (reload || !('limitsLoaded' in this.options)) { const response = await this.fetchTradingLimits (symbols); for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; this.markets[symbol] = this.deepExtend (this.markets[symbol], response[symbol]); } this.options['limitsLoaded'] = this.milliseconds (); } } return this.markets; } filterBySinceLimit (array, since = undefined, limit = undefined, key = 'timestamp', tail = false) { const sinceIsDefined = (since !== undefined && since !== null) if (sinceIsDefined) { array = array.filter ((entry) => entry[key] >= since) } if (limit !== undefined && limit !== null) { array = (tail && !sinceIsDefined) ? array.slice (-limit) : array.slice (0, limit) } return array } filterByValueSinceLimit (array, field, value = undefined, since = undefined, limit = undefined, key = 'timestamp', tail = false) { const valueIsDefined = value !== undefined && value !== null const sinceIsDefined = since !== undefined && since !== null // single-pass filter for both symbol and since if (valueIsDefined || sinceIsDefined) { array = Object.values (array).filter ((entry) => ((valueIsDefined ? (entry[field] === value) : true) && (sinceIsDefined ? (entry[key] >= since) : true))) } if (limit !== undefined && limit !== null) { array = ((tail && !sinceIsDefined) ? Object.values (array).slice (-limit) : Object.values (array).slice (0, limit)) } return array } filterBySymbolSinceLimit (array, symbol = undefined, since = undefined, limit = undefined) { return this.filterByValueSinceLimit (array, 'symbol', symbol, since, limit) } filterByCurrencySinceLimit (array, code = undefined, since = undefined, limit = undefined) { return this.filterByValueSinceLimit (array, 'currency', code, since, limit) } filterByArray (objects, key, values = undefined, indexed = true) { objects = Object.values (objects) // return all of them if no values were passed if (values === undefined || values === null) { return indexed ? indexBy (objects, key) : objects } const result = [] for (let i = 0; i < objects.length; i++) { if (values.includes (objects[i][key])) { result.push (objects[i]) } } return indexed ? indexBy (result, key) : result } parseTrades (trades, market = undefined, since = undefined, limit = undefined, params = {}) { // this code is commented out temporarily to catch for exchange-specific errors // if (!this.isArray (trades)) { // throw new ExchangeError (this.id + ' parseTrades expected an array in the trades argument, but got ' + typeof trades); // } let result = Object.values (trades || []).map ((trade) => this.extend (this.parseTrade (trade, market), params)) result = sortBy (result, 'timestamp') const symbol = (market !== undefined) ? market['symbol'] : undefined return this.filterBySymbolSinceLimit (result, symbol, since, limit) } parseTransactions (transactions, currency = undefined, since = undefined, limit = undefined, params = {}) { // this code is commented out temporarily to catch for exchange-specific errors // if (!this.isArray (transactions)) { // throw new ExchangeError (this.id + ' parseTransactions expected an array in the transactions argument, but got ' + typeof transactions); // } let result = Object.values (transactions || []).map ((transaction) => this.extend (this.parseTransaction (transaction, currency), params)) result = this.sortBy (result, 'timestamp'); const code = (currency !== undefined) ? currency['code'] : undefined; return this.filterByCurrencySinceLimit (result, code, since, limit); } parseLedger (data, currency = undefined, since = undefined, limit = undefined, params = {}) { let result = []; const array = Object.values (data || []); for (let i = 0; i < array.length; i++) { const itemOrItems = this.parseLedgerEntry (array[i], currency); if (Array.isArray (itemOrItems)) { for (let j = 0; j < itemOrItems.length; j++) { result.push (this.extend (itemOrItems[j], params)); } } else { result.push (this.extend (itemOrItems, params)); } } result = this.sortBy (result, 'timestamp'); const code = (currency !== undefined) ? currency['code'] : undefined; return this.filterByCurrencySinceLimit (result, code, since, limit); } parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { // this code is commented out temporarily to catch for exchange-specific errors // if (!this.isArray (orders)) { // throw new ExchangeError (this.id + ' parseOrders expected an array in the orders argument, but got ' + typeof orders); // } let result = Object.values (orders).map (order => this.extend (this.parseOrder (order, market), params)) result = sortBy (result, 'timestamp') const symbol = (market !== undefined) ? market['symbol'] : undefined return this.filterBySymbolSinceLimit (result, symbol, since, limit) } safeCurrencyCode (currencyId, currency = undefined) { let code = undefined if (currencyId !== undefined) { if (this.currencies_by_id !== undefined && currencyId in this.currencies_by_id) { code = this.currencies_by_id[currencyId]['code'] } else { code = this.commonCurrencyCode (currencyId.toUpperCase ()) } } if (code === undefined && currency !== undefined) { code = currency['code'] } return code } filterBySymbol (array, symbol = undefined) { return ((symbol !== undefined) ? array.filter (entry => entry.symbol === symbol) : array) } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return Array.isArray (ohlcv) ? ohlcv.slice (0, 6) : ohlcv } parseOHLCVs (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // this code is commented out temporarily to catch for exchange-specific errors // if (!this.isArray (ohlcvs)) { // throw new ExchangeError (this.id + ' parseOHLCVs expected an array in the ohlcvs argument, but got ' + typeof ohlcvs); // } ohlcvs = Object.values (ohlcvs || []) const result = [] for (let i = 0; i < ohlcvs.length; i++) { if (limit && (result.length >= limit)) { break; } const ohlcv = this.parseOHLCV (ohlcvs[i], market, timeframe, since, limit) if (since && (ohlcv[0] < since)) { continue } result.push (ohlcv) } return this.sortBy (result, 0) } editLimitBuyOrder (id, symbol, ...args) { return this.editLimitOrder (id, symbol, 'buy', ...args) } editLimitSellOrder (id, symbol, ...args) { return this.editLimitOrder (id, symbol, 'sell', ...args) } editLimitOrder (id, symbol, ...args) { return this.editOrder (id, symbol, 'limit', ...args) } async editOrder (id, symbol, ...args) { if (!this.enableRateLimit) throw new ExchangeError (this.id + ' editOrder() requires enableRateLimit = true') await this.cancelOrder (id, symbol); return this.createOrder (symbol, ...args) } createLimitOrder (symbol, ...args) { return this.createOrder (symbol, 'limit', ...args) } createMarketOrder (symbol, ...args) { return this.createOrder (symbol, 'market', ...args) } createLimitBuyOrder (symbol, ...args) { return this.createOrder (symbol, 'limit', 'buy', ...args) } createLimitSellOrder (symbol, ...args) { return this.createOrder (symbol, 'limit', 'sell', ...args) } createMarketBuyOrder (symbol, amount, params = {}) { return this.createOrder (symbol, 'market', 'buy', amount, undefined, params) } createMarketSellOrder (symbol, amount, params = {}) { return this.createOrder (symbol, 'market', 'sell', amount, undefined, params) } costToPrecision (symbol, cost) { return decimalToPrecision (cost, ROUND, this.markets[symbol].precision.price, this.precisionMode) } priceToPrecision (symbol, price) { return decimalToPrecision (price, ROUND, this.markets[symbol].precision.price, this.precisionMode) } amountToPrecision (symbol, amount) { return decimalToPrecision (amount, TRUNCATE, this.markets[symbol].precision.amount, this.precisionMode) } feeToPrecision (symbol, fee) { return decimalToPrecision (fee, ROUND, this.markets[symbol].precision.price, this.precisionMode) } currencyToPrecision (currency, fee) { return decimalToPrecision (fee, ROUND, this.currencies[currency]['precision'], this.precisionMode); } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol] const rate = market[takerOrMaker] const cost = parseFloat (this.costToPrecision (symbol, amount * price)) return { 'type': takerOrMaker, 'currency': market['quote'], 'rate': rate, 'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)), } } // ------------------------------------------------------------------------ // web3 / 0x methods static hasWeb3 () { return ethUtil && ethAbi } checkRequiredDependencies () { if (!Exchange.hasWeb3 ()) { throw new ExchangeError ('Required dependencies missing: \nnpm i ethereumjs-util ethereumjs-abi --no-save'); } } soliditySha3 (array) { // we only support address, uint256, and string solidity types const encoded = [] for (const value of array) { if (Number.isInteger (value) || value.match (/^[0-9]+$/)) { encoded.push (this.numberToBE (this.numberToString (value), 32)) } else { const noPrefix = Exchange.remove0xPrefix (value) if (noPrefix.length === 40 && noPrefix.toLowerCase ().match (/^[0-9a-f]+$/)) { // check if it is an address encoded.push (this.base16ToBinary (noPrefix)) } else { encoded.push (this.stringToBinary (noPrefix)) } } } const concated = this.binaryConcatArray (encoded) return '0x' + this.hash (concated, 'keccak', 'hex') } getZeroExOrderHash (order) { return this.soliditySha3 ([ order['exchangeContractAddress'], // address order['maker'], // address order['taker'], // address order['makerTokenAddress'], // address order['takerTokenAddress'], // address order['feeRecipient'], // address order['makerTokenAmount'], // uint256 order['takerTokenAmount'], // uint256 order['makerFee'], // uint256 order['takerFee'], // uint256 order['expirationUnixTimestampSec'], // uint256 order['salt'], // uint256 ]); } getZeroExOrderHashV2 (order) { // https://github.com/0xProject/0x-monorepo/blob/development/python-packages/order_utils/src/zero_ex/order_utils/__init__.py const addressPadding = '000000000000000000000000'; const header = '1901'; const domainStructHeader = '91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766f0f24618f4c4be1e62e026fb039a20ef96f4495294817d1027ffaa6d1f70e61ead7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5'; const orderSchemaHash = '770501f88a26ede5c04a20ef877969e961eb11fc13b78aaf414b633da0d4f86f'; const domainStructHash = ethAbi.soliditySHA3 ( [ 'bytes', 'bytes', 'address' ], [ Buffer.from (domainStructHeader, 'hex'), Buffer.from (addressPadding, 'hex'), order['exchangeAddress'] ] ); const orderStructHash = ethAbi.soliditySHA3 ( [ 'bytes', 'bytes', 'address', 'bytes', 'address', 'bytes', 'address', 'bytes', 'address', 'uint256', 'uint256', 'uint256', 'uint256', 'uint256', 'uint256', 'string', 'string' ], [ Buffer.from (orderSchemaHash, 'hex'), Buffer.from (addressPadding, 'hex'), order['makerAddress'], Buffer.from (addressPadding, 'hex'), order['takerAddress'], Buffer.from (addressPadding, 'hex'), order['feeRecipientAddress'], Buffer.from (addressPadding, 'hex'), order['senderAddress'], order['makerAssetAmount'], order['takerAssetAmount'], order['makerFee'], order['takerFee'], order['expirationTimeSeconds'], order['salt'], ethUtil.keccak (Buffer.from (order['makerAssetData'].slice (2), 'hex')), ethUtil.keccak (Buffer.from (order['takerAssetData'].slice (2), 'hex')), ] ); return '0x' + ethUtil.keccak (Buffer.concat ([ Buffer.from (header, 'hex'), domainStructHash, orderStructHash ])).toString ('hex'); } signZeroExOrderV2 (order, privateKey) { const orderHash = this.getZeroExOrderHashV2 (order); const signature = this.signMessage (orderHash, privateKey); return this.extend (order, { 'orderHash': orderHash, 'signature': this.convertECSignatureToSignatureHex (signature), }) } convertECSignatureToSignatureHex (signature) { // https://github.com/0xProject/0x-monorepo/blob/development/packages/order-utils/src/signature_utils.ts let v = signature.v; if (v !== 27 && v !== 28) { v = v + 27; } return '0x' + v.toString (16) + signature['r'].slice (-64) + signature['s'].slice (-64) + '03' } static remove0xPrefix (hexData) { if (hexData.slice (0, 2) === '0x') { return hexData.slice (2) } else { return hexData } } hashMessage (message) { // takes a hex encoded message const binaryMessage = this.base16ToBinary (Exchange.remove0xPrefix (message)) const prefix = this.stringToBinary ('\x19Ethereum Signed Message:\n' + binaryMessage.sigBytes) return '0x' + this.hash (this.binaryConcat (prefix, binaryMessage), 'keccak', 'hex') } signHash (hash, privateKey) { const signature = this.ecdsa (hash.slice (-64), privateKey.slice (-64), 'secp256k1', undefined) return { 'r': '0x' + signature['r'], 's': '0x' + signature['s'], 'v': 27 + signature['v'], } } signMessage (message, privateKey) { return this.signHash (this.hashMessage (message), privateKey.slice (-64)) } oath () { if (typeof this.twofa !== 'undefined') { return this.totp (this.twofa) } else { throw new ExchangeError (this.id + ' this.twofa has not been set') } } // the following functions take and return numbers represented as strings // this is useful for arbitrary precision maths that floats lack integerDivide (a, b) { return new BN (a).div (new BN (b)) } integerModulo (a, b) { return new BN (a).mod (new BN (b)) } integerPow (a, b) { return new BN (a).pow (new BN (b)) } } }).call(this,require("buffer").Buffer) },{"../static_dependencies/BN/bn":119,"./errors":9,"./functions":10,"buffer":178}],8:[function(require,module,exports){ 'use strict'; const errorHierarchy = { 'BaseError': { 'ExchangeError': { 'AuthenticationError': { 'PermissionDenied': {}, 'AccountSuspended': {}, }, 'ArgumentsRequired': {}, 'BadRequest': { 'BadSymbol': {}, }, 'BadResponse': { 'NullResponse': {}, }, 'InsufficientFunds': {}, 'InvalidAddress': { 'AddressPending': {}, }, 'InvalidOrder': { 'OrderNotFound': {}, 'OrderNotCached': {}, 'CancelPending': {}, 'OrderImmediatelyFillable': {}, 'OrderNotFillable': {}, 'DuplicateOrderId': {}, }, 'NotSupported': {}, }, 'NetworkError': { 'DDoSProtection': { 'RateLimitExceeded': {}, }, 'ExchangeNotAvailable': { 'OnMaintenance': {}, }, 'InvalidNonce': {}, 'RequestTimeout': {}, }, }, }; module.exports = errorHierarchy; },{}],9:[function(require,module,exports){ 'use strict'; const errorHierarchy = require ('./errorHierarchy.js') /* ------------------------------------------------------------------------ */ function subclass (BaseClass, classes, namespace = {}) { for (const [className, subclasses] of Object.entries (classes)) { const Class = Object.assign (namespace, { /* By creating a named property, we trick compiler to assign our class constructor function a name. Otherwise, all our error constructors would be shown as [Function: Error] in the debugger! And the super-useful `e.constructor.name` magic wouldn't work — we then would have no chance to obtain a error type string from an error instance programmatically! */ [className]: class extends BaseClass { constructor (message) { super (message) /* A workaround to make `instanceof` work on custom Error classes in transpiled ES5. See my blog post for the explanation of this hack: https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801 */ this.constructor = Class this.__proto__ = Class.prototype this.name = className this.message = message } } })[className] subclass (Class, subclasses, namespace) } return namespace } /* ------------------------------------------------------------------------ */ module.exports = subclass ( // Root class Error, // Derived class hierarchy errorHierarchy ) },{"./errorHierarchy.js":8}],10:[function(require,module,exports){ 'use strict'; /* ------------------------------------------------------------------------ */ const { unCamelCase } = require ('./functions/string') const unCamelCasePropertyNames = x => { for (const k in x) x[unCamelCase (k)] = x[k] // camel_case_method = camelCaseMethod return x } /* ------------------------------------------------------------------------ */ module.exports = unCamelCasePropertyNames (Object.assign ({} , require ('./functions/platform') , require ('./functions/generic') , require ('./functions/string') , require ('./functions/type') , require ('./functions/number') , require ('./functions/encode') , require ('./functions/crypto') , require ('./functions/time') , require ('./functions/throttle') , require ('./functions/misc') )) /* ------------------------------------------------------------------------ */ },{"./functions/crypto":11,"./functions/encode":12,"./functions/generic":13,"./functions/misc":14,"./functions/number":15,"./functions/platform":16,"./functions/string":17,"./functions/throttle":18,"./functions/time":19,"./functions/type":20}],11:[function(require,module,exports){ 'use strict' /* ------------------------------------------------------------------------ */ const CryptoJS = require ('../../static_dependencies/crypto-js/crypto-js') const { capitalize } = require ('./string') const { stringToBase64, utf16ToBase64, urlencodeBase64 } = require ('./encode') const NodeRSA = require ('./../../static_dependencies/node-rsa/NodeRSA') const { numberToLE } = require ('./encode') const EC = require ('./../../static_dependencies/elliptic/lib/elliptic').ec const { ArgumentsRequired } = require ('./../errors') const BN = require ('../../static_dependencies/BN/bn.js') /* ------------------------------------------------------------------------ */ const hash = (request, hash = 'md5', digest = 'hex') => { const options = {} if (hash === 'keccak') { hash = 'SHA3' options['outputLength'] = 256 } const result = CryptoJS[hash.toUpperCase ()] (request, options) return (digest === 'binary') ? result : result.toString (CryptoJS.enc[capitalize (digest)]) } /* ............................................. */ const hmac = (request, secret, hash = 'sha256', digest = 'hex') => { const result = CryptoJS['Hmac' + hash.toUpperCase ()] (request, secret) if (digest) { const encoding = (digest === 'binary') ? 'Latin1' : capitalize (digest) return result.toString (CryptoJS.enc[capitalize (encoding)]) } return result } /* ............................................. */ function rsa (request, secret, alg = 'RS256') { const algos = { 'RS256': 'pkcs1-sha256', 'RS512': 'pkcs1-sha512', } if (!(alg in algos)) { throw new ExchangeError (alg + ' is not a supported rsa signing algorithm.') } const algorithm = algos[alg] let key = new NodeRSA (secret, { 'environment': 'browser', 'signingScheme': algorithm, }) return key.sign (request, 'base64', 'binary') } /** * @return {string} */ function jwt (request, secret, alg = 'HS256') { const algos = { 'HS256': 'sha256', 'HS384': 'sha384', 'HS512': 'sha512', }; const encodedHeader = urlencodeBase64 (stringToBase64 (JSON.stringify ({ 'alg': alg, 'typ': 'JWT' }))) const encodedData = urlencodeBase64 (stringToBase64 (JSON.stringify (request))) const token = [ encodedHeader, encodedData ].join ('.') const algoType = alg.slice (0, 2); const algorithm = algos[alg] let signature = undefined if (algoType === 'HS') { signature = urlencodeBase64 (hmac (token, secret, algorithm, 'base64')) } else if (algoType === 'RS') { signature = urlencodeBase64 (rsa (token, secret, alg)) } return [ token, signature ].join ('.') } function ecdsa (request, secret, algorithm = 'p256', hashFunction = undefined, fixedLength = false) { let digest = request if (hashFunction !== undefined) { digest = hash (request, hashFunction, 'hex') } const curve = new EC (algorithm) let signature = curve.sign (digest, secret, 'hex', { 'canonical': true }) let counter = new BN ('0') const minimum_size = new BN ('1').shln (8 * 31).sub (new BN ('1')) while (fixedLength && (signature.r.gt (curve.nh) || signature.r.lte (minimum_size) || signature.s.lte (minimum_size))) { signature = curve.sign (digest, secret, 'hex', { 'canonical': true, 'extraEntropy': counter.toArray ('le', 32)}) counter = counter.add (new BN ('1')) } return { 'r': signature.r.toString (16).padStart (64, '0'), 's': signature.s.toString (16).padStart (64, '0'), 'v': signature.recoveryParam, } } /* ------------------------------------------------------------------------ */ const totp = (secret) => { const dec2hex = s => ((s < 15.5 ? '0' : '') + Math.round (s).toString (16)) , hex2dec = s => parseInt (s, 16) , leftpad = (s, p) => (p + s).slice (-p.length) // both s and p are short strings const base32tohex = (base32) => { let base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' let bits = '' let hex = '' for (let i = 0; i < base32.length; i++) { let val = base32chars.indexOf (base32.charAt (i).toUpperCase ()) bits += leftpad (val.toString (2), '00000') } for (let i = 0; i + 4 <= bits.length; i += 4) { let chunk = bits.substr (i, 4) hex = hex + parseInt (chunk, 2).toString (16) } return hex } const getOTP = (secret) => { secret = secret.replace (' ', '') // support 2fa-secrets with spaces like "4TDV WOGO" → "4TDVWOGO" let epoch = Math.round (new Date ().getTime () / 1000.0) let time = leftpad (dec2hex (Math.floor (epoch / 30)), '0000000000000000') let hmacRes = hmac (CryptoJS.enc.Hex.parse (time), CryptoJS.enc.Hex.parse (base32tohex (secret)), 'sha1', 'hex') let offset = hex2dec (hmacRes.substring (hmacRes.length - 1)) let otp = (hex2dec (hmacRes.substr (offset * 2, 8)) & hex2dec ('7fffffff')) + '' otp = (otp).substr (otp.length - 6, 6) return otp } return getOTP (secret) } /* ------------------------------------------------------------------------ */ module.exports = { hash, hmac, jwt, totp, rsa, ecdsa, } /* ------------------------------------------------------------------------ */ },{"../../static_dependencies/BN/bn.js":119,"../../static_dependencies/crypto-js/crypto-js":120,"./../../static_dependencies/elliptic/lib/elliptic":121,"./../../static_dependencies/node-rsa/NodeRSA":136,"./../errors":9,"./encode":12,"./string":17}],12:[function(require,module,exports){ "use strict"; /* ------------------------------------------------------------------------ */ const CryptoJS = require ('../../static_dependencies/crypto-js/crypto-js') const qs = require ('../../static_dependencies/qs/index') const BN = require ('../../static_dependencies/BN/bn') /* ------------------------------------------------------------------------ */ module.exports = { json: (data, params = undefined) => JSON.stringify (data) , unjson: JSON.parse , isJsonEncodedObject: object => ( (typeof object === 'string') && (object.length >= 2) && ((object[0] === '{') || (object[0] === '[')) ) , stringToBinary (str) { const arr = new Uint8Array (str.length) for (let i = 0; i < str.length; i++) { arr[i] = str.charCodeAt (i); } return CryptoJS.lib.WordArray.create (arr) } , stringToBase64: string => CryptoJS.enc.Latin1.parse (string).toString (CryptoJS.enc.Base64) , utf16ToBase64: string => CryptoJS.enc.Utf16 .parse (string).toString (CryptoJS.enc.Base64) , base64ToBinary: string => CryptoJS.enc.Base64.parse (string) , base64ToString: string => CryptoJS.enc.Base64.parse (string).toString (CryptoJS.enc.Utf8) , binaryToBase64: binary => binary.toString (CryptoJS.enc.Base64) , base16ToBinary: string => CryptoJS.enc.Hex.parse (string) , binaryToBase16: binary => binary.toString (CryptoJS.enc.Hex) , binaryConcat: (...args) => args.reduce ((a, b) => a.concat (b)) , binaryConcatArray: (arr) => arr.reduce ((a, b) => a.concat (b)) , urlencode: object => qs.stringify (object) , urlencodeWithArrayRepeat: object => qs.stringify (object, { arrayFormat: 'repeat' }) , rawencode: object => qs.stringify (object, { encode: false }) , encode: x => x , decode: x => x // Url-safe-base64 without equals signs, with + replaced by - and slashes replaced by underscores , urlencodeBase64: base64string => base64string.replace (/[=]+$/, '') .replace (/\+/g, '-') .replace (/\//g, '_') , numberToLE: (n, padding) => { const hexArray = new BN (n).toArray ('le', padding) return byteArrayToWordArray (hexArray) } , numberToBE: (n, padding) => { const hexArray = new BN (n).toArray ('be', padding) return byteArrayToWordArray (hexArray) } } function byteArrayToWordArray(ba) { const wa = [] for (let i = 0; i < ba.length; i++) { wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i) } return CryptoJS.lib.WordArray.create (wa, ba.length) } module.exports['byteArrayToWordArray'] = byteArrayToWordArray /* ------------------------------------------------------------------------ */ },{"../../static_dependencies/BN/bn":119,"../../static_dependencies/crypto-js/crypto-js":120,"../../static_dependencies/qs/index":152}],13:[function(require,module,exports){ "use strict"; /* ------------------------------------------------------------------------ */ const { isObject, isNumber, isDictionary, isArray } = require ('./type') /* ------------------------------------------------------------------------ */ const keys = Object.keys , values = x => !isArray (x) // don't copy arrays if they're already arrays! ? Object.values (x) : x , index = x => new Set (values (x)) , extend = (...args) => Object.assign ({}, ...args) // NB: side-effect free , clone = x => isArray (x) ? Array.from (x) // clones arrays : extend (x) // clones objects /* ------------------------------------------------------------------------ */ module.exports = { keys , values , extend , clone , index , ordered: x => x // a stub to keep assoc keys in order (in JS it does nothing, it's mostly for Python) , unique: x => Array.from (index (x)) , arrayConcat: (a, b) => a.concat (b) /* ............................................. */ , inArray (needle, haystack) { return haystack.includes (needle) } , toArray (object) { return Object.values (object) } , isEmpty (object) { if (!object) return true; return (Array.isArray (object) ? object : Object.keys (object)).length < 1; } /* ............................................. */ , keysort (x, out = {}) { for (const k of keys (x).sort ()) out[k] = x[k] return out } /* ............................................. */ /* Accepts a map/array of objects and a key name to be used as an index: array = [ { someKey: 'value1', anotherKey: 'anotherValue1' }, { someKey: 'value2', anotherKey: 'anotherValue2' }, { someKey: 'value3', anotherKey: 'anotherValue3' }, ] key = 'someKey' Returns a map: { value1: { someKey: 'value1', anotherKey: 'anotherValue1' }, value2: { someKey: 'value2', anotherKey: 'anotherValue2' }, value3: { someKey: 'value3', anotherKey: 'anotherValue3' }, } */ , indexBy (x, k, out = {}) { for (const v of values (x)) if (k in v) out[v[k]] = v return out } /* ............................................. */ /* Accepts a map/array of objects and a key name to be used as a grouping parameter: array = [ { someKey: 'value1', anotherKey: 'anotherValue1' }, { someKey: 'value1', anotherKey: 'anotherValue2' }, { someKey: 'value3', anotherKey: 'anotherValue3' }, ] key = 'someKey' Returns a map: { value1: [ { someKey: 'value1', anotherKey: 'anotherValue1' }, { someKey: 'value1', anotherKey: 'anotherValue2' }, ] value3: [ { someKey: 'value3', anotherKey: 'anotherValue3' } ], } */ , groupBy (x, k, out = {}) { for (const v of values (x)) { if (k in v) { const p = v[k] out[p] = out[p] || [] out[p].push (v) } } return out } /* ............................................. */ /* Accepts a map/array of objects, a key name and a key value to be used as a filter: array = [ { someKey: 'value1', anotherKey: 'anotherValue1' }, { someKey: 'value2', anotherKey: 'anotherValue2' }, { someKey: 'value3', anotherKey: 'anotherValue3' }, ] key = 'someKey' value = 'value1' Returns an array: [ value1: { someKey: 'value1', anotherKey: 'anotherValue1' }, ] */ , filterBy (x, k, value = undefined, out = []) { for (const v of values (x)) if (v[k] === value) out.push (v) return out } /* ............................................. */ , sortBy: (array, // NB: MUTATES ARRAY! key, descending = false, direction = descending ? -1 : 1) => array.sort ((a, b) => ((a[key] < b[key]) ? -direction : ((a[key] > b[key]) ? direction : 0))) /* ............................................. */ , flatten: function flatten (x, out = []) { for (const v of x) { if (isArray (v)) flatten (v, out) else out.push (v) } return out } /* ............................................. */ , pluck: (x, k) => values (x) .filter (v => k in v) .map (v => v[k]) /* ............................................. */ , omit (x, ...args) { if (!Array.isArray (x)) { const out = clone (x) for (const k of args) { if (isArray (k)) { // omit (x, ['a', 'b']) for (const kk of k) { delete out[kk] } } else delete out[k] // omit (x, 'a', 'b') } return out } return x } /* ............................................. */ , sum (...xs) { const ns = xs.filter (isNumber) // leave only numbers return (ns.length > 0) ? ns.reduce ((a, b) => a + b, 0) : undefined } /* ............................................. */ , deepExtend: function deepExtend (...xs) { let out = undefined for (const x of xs) { if (isDictionary (x)) { if (!isObject (out)) { out = {} } for (const k in x) { out[k] = deepExtend (out[k], x[k]) } } else { out = x } } return out } /* ------------------------------------------------------------------------ */ } },{"./type":20}],14:[function(require,module,exports){ 'use strict'; const { ROUND_UP, ROUND_DOWN } = require ('./number') const { NotSupported } = require ('./../errors') //------------------------------------------------------------------------- // converts timeframe to seconds const parseTimeframe = (timeframe) => { const amount = timeframe.slice (0, -1) const unit = timeframe.slice (-1) let scale = undefined; if (unit === 'y') { scale = 60 * 60 * 24 * 365 } else if (unit === 'M') { scale = 60 * 60 * 24 * 30 } else if (unit === 'w') { scale = 60 * 60 * 24 * 7 } else if (unit === 'd') { scale = 60 * 60 * 24 } else if (unit === 'h') { scale = 60 * 60 } else if (unit === 'm') { scale = 60 } else if (unit === 's') { scale = 1 } else { throw new NotSupported ('timeframe unit ' + unit + ' is not supported') } return amount * scale } const roundTimeframe = (timeframe, timestamp, direction = ROUND_DOWN) => { const ms = parseTimeframe (timeframe) * 1000 // Get offset based on timeframe in milliseconds const offset = timestamp % ms return timestamp - offset + ((direction === ROUND_UP) ? ms : 0); } // given a sorted arrays of trades (recent last) and a timeframe builds an array of OHLCV candles const buildOHLCVC = (trades, timeframe = '1m', since = -Infinity, limit = Infinity) => { const ms = parseTimeframe (timeframe) * 1000; const ohlcvs = []; const [ timestamp, /* open */, high, low, close, volume, count ] = [ 0, 1, 2, 3, 4, 5, 6 ]; const oldest = Math.min (trades.length - 1, limit); for (let i = 0; i <= oldest; i++) { const trade = trades[i]; if (trade.timestamp < since) continue; const openingTime = Math.floor (trade.timestamp / ms) * ms; // shift to the edge of m/h/d (but not M) const candle = ohlcvs.length - 1; if (candle === -1 || openingTime >= ohlcvs[candle][timestamp] + ms) { // moved to a new timeframe -> create a new candle from opening trade ohlcvs.push ([ openingTime, // timestamp trade.price, // O trade.price, // H trade.price, // L trade.price, // C trade.amount, // V 1, // count ]); } else { // still processing the same timeframe -> update opening trade ohlcvs[candle][high] = Math.max (ohlcvs[candle][high], trade.price); ohlcvs[candle][low] = Math.min (ohlcvs[candle][low], trade.price); ohlcvs[candle][close] = trade.price; ohlcvs[candle][volume] += trade.amount; ohlcvs[candle][count]++; } // if } // for return ohlcvs; } const extractParams = (string) => { const re = /{([\w-]+)}/g const matches = [] let match = re.exec (string) while (match) { matches.push (match[1]) match = re.exec (string) } return matches } const implodeParams = (string, params) => { if (!Array.isArray (params)) { const keys = Object.keys (params) for (let i = 0; i < keys.length; i++) { const key = keys[i] if (!Array.isArray (params[key])) { string = string.replace ('{' + key + '}', params[key]) } } } return string } /* ------------------------------------------------------------------------ */ module.exports = { aggregate (bidasks) { const result = {} for (let i = 0; i < bidasks.length; i++) { const [ price, volume ] = bidasks[i]; if (volume > 0) { result[price] = (result[price] || 0) + volume } } return Object.keys (result).map (price => [parseFloat (price), parseFloat (result[price])]) }, parseTimeframe, roundTimeframe, buildOHLCVC, ROUND_UP, ROUND_DOWN, implodeParams, extractParams } /* ------------------------------------------------------------------------ */ },{"./../errors":9,"./number":15}],15:[function(require,module,exports){ 'use strict' /* ------------------------------------------------------------------------ NB: initially, I used objects for options passing: decimalToPrecision ('123.456', { digits: 2, round: true, afterPoint: true }) ...but it turns out it's hard to port that across different languages and it is also probably has a performance penalty -- while it's a performance critical code! So I switched to using named constants instead, as it is actually more readable and succinct, and surely doesn't come with any inherent performance downside: decimalToPrecision ('123.456', ROUND, 2, DECIMAL_PLACES) */ const ROUND = 0 // rounding mode , TRUNCATE = 1 , ROUND_UP = 2 , ROUND_DOWN = 3 const DECIMAL_PLACES = 0 // digits counting mode , SIGNIFICANT_DIGITS = 1 , TICK_SIZE = 2 const NO_PADDING = 0 // zero-padding mode , PAD_WITH_ZERO = 1 const precisionConstants = { ROUND, TRUNCATE, ROUND_UP, ROUND_DOWN, DECIMAL_PLACES, SIGNIFICANT_DIGITS, TICK_SIZE, NO_PADDING, PAD_WITH_ZERO, } /* ------------------------------------------------------------------------ */ // See https://stackoverflow.com/questions/1685680/how-to-avoid-scientific-notation-for-large-numbers-in-javascript for discussion function numberToString (x) { // avoids scientific notation for too large and too small numbers if (typeof x === 'string') return x const s = x.toString () if (Math.abs (x) < 1.0) { const e = parseInt (s.split ('e-')[1]) const neg = (s[0] === '-') if (e) { x *= Math.pow (10, e - 1) x = (neg ? '-' : '') + '0.' + (new Array (e)).join ('0') + x.toString ().substring (neg ? 3 : 2) return x } } else { const parts = s.split ('e') if (parts[1]) { let e = parseInt (parts[1]) const m = parts[0].split ('.') if (m[1]) { e -= m[1].length } return m[0] + m[1] + (new Array (e + 1)).join ('0') } } return s; } //----------------------------------------------------------------------------- // expects non-scientific notation const truncate_regExpCache = [] , truncate_to_string = (num, precision = 0) => { num = numberToString (num) if (precision > 0) { const re = truncate_regExpCache[precision] || (truncate_regExpCache[precision] = new RegExp ("([-]*\\d+\\.\\d{" + precision + "})(\\d)")) const [ , result] = num.toString ().match (re) || [null, num] return result.toString () } return parseInt (num).toString () } , truncate = (num, precision = 0) => parseFloat (truncate_to_string (num, precision)) function precisionFromString (string) { const split = string.replace (/0+$/g, '').split ('.') return (split.length > 1) ? (split[1].length) : 0 } /* ------------------------------------------------------------------------ */ const decimalToPrecision = (x, roundingMode , numPrecisionDigits , countingMode = DECIMAL_PLACES , paddingMode = NO_PADDING) => { if (numPrecisionDigits < 0) { if (countingMode === TICK_SIZE) { throw new Error (`TICK_SIZE cant be used with negative numPrecisionDigits`) } const toNearest = Math.pow (10, -numPrecisionDigits) if (roundingMode === ROUND) { return (toNearest * decimalToPrecision (x / toNearest, roundingMode, 0, countingMode, paddingMode)).toString () } if (roundingMode === TRUNCATE) { return (x - (x % toNearest)).toString () } } /* handle tick size */ if (countingMode === TICK_SIZE) { const precisionDigitsString = decimalToPrecision (numPrecisionDigits, ROUND, 100, DECIMAL_PLACES, NO_PADDING) const newNumPrecisionDigits = precisionFromString (precisionDigitsString) let missing = x % numPrecisionDigits // See: https://github.com/ccxt/ccxt/pull/6486 missing = Number (decimalToPrecision (missing, ROUND, 8, DECIMAL_PLACES, NO_PADDING)); const fpError = decimalToPrecision (missing / numPrecisionDigits, ROUND, Math.max (newNumPrecisionDigits, 8), DECIMAL_PLACES, NO_PADDING) if (precisionFromString (fpError) !== 0) { if (roundingMode === ROUND) { if (x > 0) { if (missing >= numPrecisionDigits / 2) { x = x - missing + numPrecisionDigits } else { x = x - missing } } else { if (missing >= numPrecisionDigits / 2) { x = Number (x) - missing } else { x = Number (x) - missing - numPrecisionDigits } } } else if (roundingMode === TRUNCATE) { x = x - missing } } return decimalToPrecision (x, ROUND, newNumPrecisionDigits, DECIMAL_PLACES, paddingMode); } /* Convert to a string (if needed), skip leading minus sign (if any) */ const str = numberToString (x) , isNegative = str[0] === '-' , strStart = isNegative ? 1 : 0 , strEnd = str.length /* Find the dot position in the source buffer */ for (var strDot = 0; strDot < strEnd; strDot++) if (str[strDot] === '.') break const hasDot = strDot < str.length /* Char code constants */ const MINUS = 45 , DOT = 46 , ZERO = 48 , ONE = (ZERO + 1) , FIVE = (ZERO + 5) , NINE = (ZERO + 9) /* For -123.4567 the `chars` array will hold 01234567 (leading zero is reserved for rounding cases when 099 → 100) */ const chars = new Uint8Array ((strEnd - strStart) + (hasDot ? 0 : 1)) chars[0] = ZERO /* Validate & copy digits, determine certain locations in the resulting buffer */ let afterDot = chars.length , digitsStart = -1 // significant digits , digitsEnd = -1 for (var i = 1, j = strStart; j < strEnd; j++, i++) { const c = str.charCodeAt (j) if (c === DOT) { afterDot = i-- } else if ((c < ZERO) || (c > NINE)) { throw new Error (`${str}: invalid number (contains an illegal character '${str[i - 1]}')`) } else { chars[i] = c if ((c !== ZERO) && (digitsStart < 0)) digitsStart = i } } if (digitsStart < 0) digitsStart = 1 /* Determine the range to cut */ let precisionStart = (countingMode === DECIMAL_PLACES) ? afterDot // 0.(0)001234567 : digitsStart // 0.00(1)234567 , precisionEnd = precisionStart + numPrecisionDigits /* Reset the last significant digit index, as it will change during the rounding/truncation. */ digitsEnd = -1 /* Perform rounding/truncation per digit, from digitsEnd to digitsStart, by using the following algorithm (rounding 999 → 1000, as an example): step = i=3 i=2 i=1 i=0 chars = 0999 0999 0900 1000 memo = ---0 --1- -1-- 0--- */ let allZeros = true; let signNeeded = isNegative; for (let i = chars.length - 1, memo = 0; i >= 0; i--) { let c = chars[i] if (i !== 0) { c += memo if (i >= (precisionStart + numPrecisionDigits)) { const ceil = (roundingMode === ROUND) && (c >= FIVE) && !((c === FIVE) && memo) // prevents rounding of 1.45 to 2 c = ceil ? (NINE + 1) : ZERO } if (c > NINE) { c = ZERO; memo = 1; } else memo = 0 } else if (memo) c = ONE // leading extra digit (0900 → 1000) chars[i] = c if (c !== ZERO) { allZeros = false digitsStart = i digitsEnd = (digitsEnd < 0) ? (i + 1) : digitsEnd } } /* Update the precision range, as `digitsStart` may have changed... & the need for a negative sign if it is only 0 */ if (countingMode === SIGNIFICANT_DIGITS) { precisionStart = digitsStart precisionEnd = precisionStart + numPrecisionDigits } if (allZeros) { signNeeded = false } /* Determine the input character range */ const readStart = ((digitsStart >= afterDot) || allZeros) ? (afterDot - 1) : digitsStart // 0.000(1)234 ----> (0).0001234 , readEnd = (digitsEnd < afterDot) ? (afterDot ) : digitsEnd // 12(3)000 ----> 123000( ) /* Compute various sub-ranges */ const nSign = (signNeeded ? 1 : 0) // (-)123.456 , nBeforeDot = (nSign + (afterDot - readStart)) // (-123).456 , nAfterDot = Math.max (readEnd - afterDot, 0) // -123.(456) , actualLength = (readEnd - readStart) // -(123.456) , desiredLength = (paddingMode === NO_PADDING) ? (actualLength) // -(123.456) : (precisionEnd - readStart) // -(123.456 ) , pad = Math.max (desiredLength - actualLength, 0) // -123.456( ) , padStart = (nBeforeDot + 1 + nAfterDot) // -123.456( ) , padEnd = (padStart + pad) // -123.456 ( ) , isInteger = (nAfterDot + pad) === 0 // -123 /* Fill the output buffer with characters */ const out = new Uint8Array (nBeforeDot + (isInteger ? 0 : 1) + nAfterDot + pad) // ------------------------------------------------------------------------------------------ // --------------------- if (signNeeded) out[0] = MINUS // - minus sign for (i = nSign, j = readStart; i < nBeforeDot; i++, j++) out[i] = chars[j] // 123 before dot if (!isInteger) out[nBeforeDot] = DOT // . dot for (i = nBeforeDot + 1, j = afterDot; i < padStart; i++, j++) out[i] = chars[j] // 456 after dot for (i = padStart; i < padEnd; i++) out[i] = ZERO // 000 padding /* Build a string from the output buffer */ return String.fromCharCode (...out) } // toWei / fromWei function fromWei (amount, decimals = 18) { if (amount === undefined) { return amount } const exponential = Math.floor (amount).toExponential () // wei must be whole numbers const [ n, exponent ] = exponential.split ('e') const newExponent = parseInt (exponent) - decimals return parseFloat (n + 'e' + newExponent) } function toWei (amount, decimals = 18) { if (amount === undefined) { return amount } const exponential = parseFloat (amount).toExponential () const [ n, exponent ] = exponential.split ('e') const newExponent = parseInt (exponent) + decimals return numberToString (Math.floor (parseFloat (n + 'e' + newExponent))) // wei must be whole numbers } /* ------------------------------------------------------------------------ */ module.exports = { toWei, fromWei, numberToString, precisionFromString, decimalToPrecision, truncate_to_string, truncate, precisionConstants, ROUND, TRUNCATE, ROUND_UP, ROUND_DOWN, DECIMAL_PLACES, SIGNIFICANT_DIGITS, TICK_SIZE, NO_PADDING, PAD_WITH_ZERO, } /* ------------------------------------------------------------------------ */ },{}],16:[function(require,module,exports){ (function (process){ "use strict"; // ---------------------------------------------------------------------------- // There's been a lot of messing with this code... // The problem is to satisfy the following requirements: // - properly detect isNode == true on server side and isNode == false in the browser (on client side) // - make sure create-react-app, react-starter-kit and other react frameworks work // - make sure it does not break the browserified version (when linked into a html from a cdn) // - make sure it does not break the webpacking and babel-transpiled scripts // - make sure it works in Electron // - make sure it works with Angular.js // - make sure it does not break other possible usage scenarios const isBrowser = typeof window !== 'undefined' const isElectron = typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' const isWebWorker = typeof WorkerGlobalScope !== 'undefined' && (self instanceof WorkerGlobalScope) const isWindows = typeof process !== 'undefined' && process.platform === "win32" const isNode = !(isBrowser || isWebWorker) const defaultFetch = typeof (fetch) === "undefined" ? require ('../../static_dependencies/fetch-ponyfill/fetch-node') ().fetch : fetch // ---------------------------------------------------------------------------- module.exports = { isBrowser, isElectron, isWebWorker, isNode, isWindows, defaultFetch, } }).call(this,require('_process')) },{"../../static_dependencies/fetch-ponyfill/fetch-node":134,"_process":200}],17:[function(require,module,exports){ "use strict"; /* ------------------------------------------------------------------------ */ const uuid = a => a ? (a ^ Math.random () * 16 >> a / 4).toString (16) : ([1e7]+-1e3+-4e3+-8e3+-1e11).replace (/[018]/g, uuid) module.exports = { uuid // hasFetchOHLCV → has_fetch_ohlcv; parseHTTPResponse → parse_http_response , unCamelCase: s => s.match (/^[A-Z0-9_]+$/) ? s : (s.replace (/[a-z0-9][A-Z]/g, x => x[0] + '_' + x[1]).replace(/[A-Z0-9][A-Z0-9][a-z]/g, x => x[0] + '_' + x[1] + x[2]).toLowerCase ()) , capitalize: s => s.length ? (s.charAt (0).toUpperCase () + s.slice (1)) : s , strip: s => s.replace(/^\s+|\s+$/g, '') } /* ------------------------------------------------------------------------ */ },{}],18:[function(require,module,exports){ "use strict"; /* ------------------------------------------------------------------------ */ const { sleep , now } = require ('./time') /* ------------------------------------------------------------------------ */ module.exports = { throttle: function throttle (cfg) { let lastTimestamp = now () , numTokens = (cfg.numTokens !== undefined) ? cfg.numTokens : cfg.capacity , running = false , counter = 0 const queue = [] return Object.assign ((rateLimit, cost) => { if (queue.length > cfg.maxCapacity) throw new Error ('Backlog is over max capacity of ' + cfg.maxCapacity) return new Promise (async (resolve, reject) => { try { queue.push ({ cost, resolve, reject }) if (!running) { running = true while (queue.length > 0) { const hasEnoughTokens = cfg.capacity ? (numTokens > 0) : (numTokens >= 0) if (hasEnoughTokens) { if (queue.length > 0) { let { cost, resolve, reject } = queue[0] cost = (cost || cfg.defaultCost) if (numTokens >= Math.min (cost, cfg.capacity)) { numTokens -= cost queue.shift () resolve () } } } const t = now () , elapsed = t - lastTimestamp lastTimestamp = t numTokens = Math.min (cfg.capacity, numTokens + elapsed * (1 / rateLimit)) await sleep (cfg.delay) } running = false } } catch (e) { reject (e) } }) }, cfg, { configure: newCfg => throttle (Object.assign ({}, cfg, newCfg)) }) } } /* ------------------------------------------------------------------------ */ },{"./time":19}],19:[function(require,module,exports){ 'use strict'; /* ------------------------------------------------------------------------ */ const now = Date.now // TODO: figure out how to utilize performance.now () properly – it's not as easy as it does not return a unix timestamp... const microseconds = () => now () * 1000 // TODO: utilize performance.now for that purpose const milliseconds = now const seconds = () => Math.floor (now () / 1000) /* ------------------------------------------------------------------------ */ const setTimeout_original = setTimeout const setTimeout_safe = (done, ms, setTimeout = setTimeout_original /* overrideable for mocking purposes */, targetTime = now () + ms) => { /* The built-in setTimeout function can fire its callback earlier than specified, so we need to ensure that it does not happen: sleep recursively until `targetTime` is reached... */ let clearInnerTimeout = () => {} let active = true const id = setTimeout (() => { active = true const rest = targetTime - now () if (rest > 0) { clearInnerTimeout = setTimeout_safe (done, rest, setTimeout, targetTime) // try sleep more } else { done () } }, ms) return function clear () { if (active) { active = false // dunno if IDs are unique on various platforms, so it's better to rely on this flag to exclude the possible cancellation of the wrong timer (if called after completion) clearTimeout (id) } clearInnerTimeout () } } /* ------------------------------------------------------------------------ */ class TimedOut extends Error { constructor () { const message = 'timed out' super (message) this.constructor = TimedOut this.__proto__ = TimedOut.prototype this.message = message } } /* ------------------------------------------------------------------------ */ const iso8601 = (timestamp) => { let _timestampNumber = undefined; if (typeof timestamp === 'number') { _timestampNumber = Math.floor (timestamp); } else { _timestampNumber = parseInt (timestamp, 10); } // undefined, null and lots of nasty non-numeric values yield NaN if (Number.isNaN (_timestampNumber) || _timestampNumber < 0) { return undefined; } // last line of defence try { return new Date (_timestampNumber).toISOString (); } catch (e) { return undefined; } } const parse8601 = (x) => { if (typeof x !== 'string' || !x) { return undefined; } if (x.match (/^[0-9]+$/)) { // a valid number in a string, not a date. return undefined; } if (x.indexOf ('-') < 0 || x.indexOf (':') < 0) { // no date can be without a dash and a colon return undefined; } // last line of defence try { const candidate = Date.parse (((x.indexOf ('+') >= 0) || (x.slice (-1) === 'Z')) ? x : (x + 'Z').replace (/\s(\d\d):/, 'T$1:')); if (Number.isNaN (candidate)) { return undefined; } return candidate; } catch (e) { return undefined; } } const parseDate = (x) => { if (typeof x !== 'string' || !x) { return undefined; } if (x.indexOf ('GMT') >= 0) { try { return Date.parse (x); } catch (e) { return undefined; } } return parse8601 (x); } const mdy = (timestamp, infix = '-') => { infix = infix || '' const date = new Date (timestamp) const Y = date.getUTCFullYear ().toString () let m = date.getUTCMonth () + 1 let d = date.getUTCDate () m = m < 10 ? ('0' + m) : m.toString () d = d < 10 ? ('0' + d) : d.toString () return m + infix + d + infix + Y } const ymd = (timestamp, infix = '-') => { infix = infix || '' const date = new Date (timestamp) const Y = date.getUTCFullYear ().toString () let m = date.getUTCMonth () + 1 let d = date.getUTCDate () m = m < 10 ? ('0' + m) : m.toString () d = d < 10 ? ('0' + d) : d.toString () return Y + infix + m + infix + d } const ymdhms = (timestamp, infix = ' ') => { const date = new Date (timestamp) const Y = date.getUTCFullYear () let m = date.getUTCMonth () + 1 let d = date.getUTCDate () let H = date.getUTCHours () let M = date.getUTCMinutes () let S = date.getUTCSeconds () m = m < 10 ? ('0' + m) : m d = d < 10 ? ('0' + d) : d H = H < 10 ? ('0' + H) : H M = M < 10 ? ('0' + M) : M S = S < 10 ? ('0' + S) : S return Y + '-' + m + '-' + d + infix + H + ':' + M + ':' + S } module.exports = { now , microseconds , milliseconds , seconds , iso8601 , parse8601 , parseDate , mdy , ymd , ymdhms , setTimeout_safe , sleep: ms => new Promise (resolve => setTimeout_safe (resolve, ms)) , TimedOut , timeout: async (ms, promise) => { let clear = () => {} const expires = new Promise (resolve => (clear = setTimeout_safe (resolve, ms))) try { return await Promise.race ([promise, expires.then (() => { throw new TimedOut () })]) } finally { clear () // fixes https://github.com/ccxt/ccxt/issues/749 } } } /* ------------------------------------------------------------------------ */ },{}],20:[function(require,module,exports){ "use strict"; /* ------------------------------------------------------------------------ */ const isNumber = Number.isFinite , isInteger = Number.isInteger , isArray = Array.isArray , hasProps = o => ((o !== undefined) && (o !== null)) , isString = s => (typeof s === 'string') , isObject = o => ((o !== null) && (typeof o === 'object')) , isDictionary = o => (isObject (o) && !isArray (o)) , isStringCoercible = x => ((hasProps (x) && x.toString) || isNumber (x)) /* ............................................. */ const prop = (o, k) => (isObject (o) ? o[k] : undefined) , prop2 = (o, k1, k2) => (!isObject (o) ? undefined : (((k1 in o) && (o[k1] !== null)) ? o[k1] : o[k2])) /* ............................................. */ const asFloat = x => ((isNumber (x) || isString (x)) ? parseFloat (x) : NaN) , asInteger = x => ((isNumber (x) || isString (x)) ? parseInt (x, 10) : NaN) /* ............................................. */ module.exports = { isNumber , isInteger , isArray , isObject , isString , isStringCoercible , isDictionary , hasProps , prop , asFloat , asInteger , safeFloat: (o, k, $default, n = asFloat (prop (o, k))) => (isNumber (n) ? n : $default) , safeInteger: (o, k, $default, n = asInteger (prop (o, k))) => (isNumber (n) ? n : $default) , safeIntegerProduct: (o, k, $factor, $default, n = asInteger (prop (o, k))) => (isNumber (n) ? parseInt (n * $factor) : $default) , safeTimestamp: (o, k, $default, n = asFloat (prop (o, k))) => (isNumber (n) ? parseInt (n * 1000) : $default) , safeValue: (o, k, $default, x = prop (o, k)) => (hasProps (x) ? x : $default) , safeString: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x) : $default) , safeStringLower: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x).toLowerCase () : $default) , safeStringUpper: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x).toUpperCase () : $default) // not using safeFloats with an array argument as we're trying to save some cycles here // we're not using safeFloat3 either because those cases are too rare to deserve their own optimization , safeFloat2: (o, k1, k2, $default, n = asFloat (prop2 (o, k1, k2))) => (isNumber (n) ? n : $default) , safeInteger2: (o, k1, k2, $default, n = asInteger (prop2 (o, k1, k2))) => (isNumber (n) ? n : $default) , safeIntegerProduct2: (o, k1, k2, $factor, $default, n = asInteger (prop2 (o, k1, k2))) => (isNumber (n) ? parseInt (n * $factor) : $default) , safeTimestamp2: (o, k1, k2, $default, n = asFloat (prop2 (o, k1, k2))) => (isNumber (n) ? parseInt (n * 1000) : $default) , safeValue2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (hasProps (x) ? x : $default) , safeString2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x) : $default) , safeStringLower2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x).toLowerCase () : $default) , safeStringUpper2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x).toUpperCase () : $default) } /* ------------------------------------------------------------------------ */ },{}],21:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder, OrderNotFound } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bcex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bcex', 'name': 'BCEX', 'countries': [ 'CN', 'CA' ], 'version': '1', 'has': { 'fetchBalance': true, 'fetchMarkets': true, 'createOrder': true, 'cancelOrder': true, 'fetchTicker': true, 'fetchTickers': false, 'fetchTrades': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchClosedOrders': 'emulated', 'fetchOpenOrders': true, 'fetchTradingLimits': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/77231516-851c6900-6bac-11ea-8fd6-ee5c23eddbd4.jpg', 'api': 'https://www.bcex.top', 'www': 'https://www.bcex.top', 'doc': 'https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface', 'fees': 'https://bcex.udesk.cn/hc/articles/57085', 'referral': 'https://www.bcex.top/register?invite_code=758978&lang=en', }, 'status': { 'status': 'error', 'updated': undefined, 'eta': undefined, 'url': undefined, }, 'api': { 'public': { 'get': [ 'Api_Market/getPriceList', // tickers 'Api_Order/ticker', // last ohlcv candle (ticker) 'Api_Order/depth', // orderbook 'Api_Market/getCoinTrade', // ticker 'Api_Order/marketOrder', // trades... ], 'post': [ 'Api_Market/getPriceList', // tickers 'Api_Order/ticker', // last ohlcv candle (ticker) 'Api_Order/depth', // orderbook 'Api_Market/getCoinTrade', // ticker 'Api_Order/marketOrder', // trades... ], }, 'private': { 'post': [ 'Api_Order/cancel', 'Api_Order/coinTrust', // limit order 'Api_Order/orderList', // open / all orders (my trades?) 'Api_Order/orderInfo', 'Api_Order/tradeList', // open / all orders 'Api_Order/trustList', // ? 'Api_User/userBalance', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.1 / 100, 'taker': 0.2 / 100, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'ckusd': 0.0, 'other': 0.05 / 100, }, 'deposit': {}, }, }, 'exceptions': { '该币不存在,非法操作': ExchangeError, // { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well) '公钥不合法': AuthenticationError, // { code: 1, msg: '公钥不合法' } - wrong public key '您的可用余额不足': InsufficientFunds, // { code: 1, msg: '您的可用余额不足' } - your available balance is insufficient '您的btc不足': InsufficientFunds, // { code: 1, msg: '您的btc不足' } - your btc is insufficient '参数非法': InvalidOrder, // {'code': 1, 'msg': '参数非法'} - 'Parameter illegal' '订单信息不存在': OrderNotFound, // {'code': 1, 'msg': '订单信息不存在'} - 'Order information does not exist' }, 'options': { 'limits': { // hardcoding is deprecated, using these predefined values is not recommended, use loadTradingLimits instead 'AFC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}}, 'AFC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}}, 'AFT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}}, 'AICC/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}}, 'AIDOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}}, 'AISI/ETH': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 500 }}}, 'AIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}}, 'ANS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 500 }}}, 'ANS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}}, 'ARC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 60, 'max': 600000 }}}, 'AXF/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 1000000 }}}, 'BASH/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 3000000 }}}, 'BATT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 60, 'max': 1500000 }}}, 'BCD/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.3, 'max': 7000 }}}, 'BHPC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 70000 }}}, 'BHPC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 60000 }}}, 'BOPO/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 2000000 }}}, 'BOPO/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 10000000 }}}, 'BTC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 10 }}}, 'BTC/CNET': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0005, 'max': 5 }}}, 'BTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0002, 'max': 4 }}}, 'BTE/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}}, 'BU/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}}, 'CIC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3000, 'max': 30000000 }}}, 'CIT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}}, 'CIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}}, 'CMT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 2500000 }}}, 'CNET/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 12, 'max': 120000 }}}, 'CNMC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 50000 }}}, 'CTC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 550000 }}}, 'CZR/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 12, 'max': 500000 }}}, 'DCON/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 8, 'max': 300000 }}}, 'DCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 40000 }}}, 'DCT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 2, 'max': 2000 }}}, 'DOGE/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 3000, 'max': 14000000 }}}, 'DOGE/CKUSD': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 500, 'max': 2000000 }}}, 'DRCT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 16, 'max': 190000 }}}, 'ELA/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 500 }}}, 'ELF/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 100000 }}}, 'ELF/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.01, 'max': 100000 }}}, 'EOS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}}, 'EOS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 30000 }}}, 'EOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.18, 'max': 1800 }}}, 'ETC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}}, 'ETC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}}, 'ETF/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 150000 }}}, 'ETH/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.015, 'max': 100 }}}, 'ETH/CKUSD': { 'precision': { 'amount': 4, 'price': 4 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}}, 'ETH/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}}, 'FCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}}, 'FCT/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}}, 'GAME/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 1, 'max': 10000 }}}, 'GOOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}}, 'GP/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 600, 'max': 6000000 }}}, 'HSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 1000, 'max': 20000000 }}}, 'IFISH/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 300, 'max': 8000000 }}}, 'IIC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 4000000 }}}, 'IMOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}}, 'JC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}}, 'LBTC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 3000 }}}, 'LEC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 500, 'max': 5000000 }}}, 'LKY/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 70000 }}}, 'LKY/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, 'LMC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}}, 'LSK/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.3, 'max': 3000 }}}, 'LTC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}}, 'LTC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}}, 'LTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 450 }}}, 'MC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 10000, 'max': 100000000 }}}, 'MCC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 30, 'max': 350000 }}}, 'MOC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 25, 'max': 600000 }}}, 'MRYC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}}, 'MT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 6000000 }}}, 'MXI/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 5000, 'max': 60000000 }}}, 'NAI/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, 'NAS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 15000 }}}, 'NAS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}}, 'NEWOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 65, 'max': 700000 }}}, 'NKN/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 3, 'max': 350000 }}}, 'NTK/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 30000 }}}, 'ONT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2000 }}}, 'ONT/ETH': { 'precision': { 'amount': 3, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 1000 }}}, 'PNT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 80, 'max': 800000 }}}, 'PST/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}}, 'PTT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 450, 'max': 10000000 }}}, 'QTUM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.4, 'max': 2800 }}}, 'QTUM/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}}, 'RATING/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}}, 'RHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}}, 'SDA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 500000 }}}, 'SDD/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, 'SHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 2500000 }}}, 'SHE/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 5000000 }}}, 'SMC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}}, 'SOP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 1000000 }}}, 'TAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 35, 'max': 800000 }}}, 'TIP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 200000 }}}, 'TKT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 40, 'max': 400000 }}}, 'TLC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}}, 'TNC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 110000 }}}, 'TUB/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 8000000 }}}, 'UC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 3000000 }}}, 'UDB/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 2000, 'max': 40000000 }}}, 'UIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}}, 'VAAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 250000 }}}, 'VPN/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}}, 'VSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 30, 'max': 650000 }}}, 'WAVES/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.15, 'max': 1500 }}}, 'WDNA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 250000 }}}, 'WIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3, 'max': 30000 }}}, 'XAS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 25000 }}}, 'XLM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 300000 }}}, 'XLM/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1, 'max': 300000 }}}, 'XLM/USDT': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}}, 'XRP/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 24, 'max': 100000 }}}, 'XRP/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}}, 'YBCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 200000 }}}, 'YBCT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 200000 }}}, 'YBY/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 25000, 'max': 250000000 }}}, 'ZEC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}}, 'ZEC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}}, }, }, }); } async fetchTradingLimits (symbols = undefined, params = {}) { // this method should not be called directly, use loadTradingLimits () instead // by default it will try load withdrawal fees of all currencies (with separate requests, sequentially) // however if you define symbols = [ 'ETH/BTC', 'LTC/BTC' ] in args it will only load those await this.loadMarkets (); if (symbols === undefined) { symbols = this.symbols; } const result = {}; for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; result[symbol] = await this.fetchTradingLimitsById (this.marketId (symbol), params); } return result; } async fetchTradingLimitsById (id, params = {}) { const request = { 'symbol': id, }; const response = await this.publicPostApiOrderTicker (this.extend (request, params)); // // { code: 0, // msg: "获取牌价信息成功", // data: { high: 0.03721392, // low: 0.03335362, // buy: "0.03525757", // sell: "0.03531160", // last: 0.0352634, // vol: "184742.4176", // min_trade: "0.01500000", // max_trade: "100.00000000", // number_float: "4", // price_float: "8" } } } // return this.parseTradingLimits (this.safeValue (response, 'data', {})); } parseTradingLimits (limits, symbol = undefined, params = {}) { // // { high: 0.03721392, // low: 0.03335362, // buy: "0.03525757", // sell: "0.03531160", // last: 0.0352634, // vol: "184742.4176", // min_trade: "0.01500000", // max_trade: "100.00000000", // number_float: "4", // price_float: "8" } // return { 'info': limits, 'precision': { 'amount': this.safeInteger (limits, 'number_float'), 'price': this.safeInteger (limits, 'price_float'), }, 'limits': { 'amount': { 'min': this.safeFloat (limits, 'min_trade'), 'max': this.safeFloat (limits, 'max_trade'), }, }, }; } async fetchMarkets (params = {}) { const response = await this.publicGetApiMarketGetPriceList (params); const result = []; const keys = Object.keys (response); for (let i = 0; i < keys.length; i++) { const currentMarketId = keys[i]; const currentMarkets = response[currentMarketId]; for (let j = 0; j < currentMarkets.length; j++) { const market = currentMarkets[j]; const baseId = this.safeString (market, 'coin_from'); const quoteId = this.safeString (market, 'coin_to'); let base = baseId.toUpperCase (); let quote = quoteId.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const id = baseId + '2' + quoteId; const symbol = base + '/' + quote; const active = true; const defaults = this.safeValue (this.options['limits'], symbol, {}); result.push (this.extend ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, // overrided by defaults from this.options['limits'] 'precision': { 'amount': undefined, 'price': undefined, }, // overrided by defaults from this.options['limits'] 'limits': { 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, }, 'info': market, }, defaults)); } } return result; } parseTrade (trade, market = undefined) { let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeTimestamp2 (trade, 'date', 'created'); const id = this.safeString (trade, 'tid'); const orderId = this.safeString (trade, 'order_id'); const amount = this.safeFloat2 (trade, 'number', 'amount'); const price = this.safeFloat (trade, 'price'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } let side = this.safeString (trade, 'side'); if (side === 'sale') { side = 'sell'; } return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'order': orderId, 'fee': undefined, 'takerOrMaker': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; if (limit !== undefined) { request['limit'] = limit; } const market = this.market (symbol); const response = await this.publicPostApiOrderMarketOrder (this.extend (request, params)); return this.parseTrades (response['data'], market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostApiUserUserBalance (params); const data = this.safeValue (response, 'data'); let keys = Object.keys (data); const result = { }; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const amount = this.safeFloat (data, key); const parts = key.split ('_'); const currencyId = parts[0]; const lockOrOver = parts[1]; const code = this.safeCurrencyCode (currencyId); if (!(code in result)) { result[code] = this.account (); } if (lockOrOver === 'lock') { result[code]['used'] = parseFloat (amount); } else { result[code]['free'] = parseFloat (amount); } } keys = Object.keys (result); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const total = this.sum (result[key]['used'], result[key]['free']); result[key]['total'] = total; } result['info'] = data; return this.parseBalance (result); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.markets[symbol]; const request = { 'part': market['quoteId'], 'coin': market['baseId'], }; const response = await this.publicPostApiMarketGetCoinTrade (this.extend (request, params)); const timestamp = this.milliseconds (); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (response, 'max'), 'low': this.safeFloat (response, 'min'), 'bid': this.safeFloat (response, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (response, 'sale'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': this.safeFloat (response, 'price'), 'last': this.safeFloat (response, 'price'), 'previousClose': undefined, 'change': undefined, 'percentage': this.safeFloat (response, 'change_24h'), 'average': undefined, 'baseVolume': this.safeFloat (response, 'volume_24h'), 'quoteVolume': undefined, 'info': response, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const marketId = this.marketId (symbol); const request = { 'symbol': marketId, }; const response = await this.publicPostApiOrderDepth (this.extend (request, params)); const data = this.safeValue (response, 'data'); const timestamp = this.safeTimestamp (data, 'date'); return this.parseOrderBook (data, timestamp); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.privatePostApiOrderOrderList (this.extend (request, params)); return this.parseTrades (response['data'], market, since, limit); } parseOrderStatus (status) { const statuses = { '0': 'open', '1': 'open', // partially filled '2': 'closed', '3': 'canceled', }; return this.safeString (statuses, status, status); } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a `symbol` argument'); } await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), 'trust_id': id, }; const response = await this.privatePostApiOrderOrderInfo (this.extend (request, params)); const order = this.safeValue (response, 'data'); const timestamp = this.safeTimestamp (order, 'created'); const status = this.parseOrderStatus (this.safeString (order, 'status')); let side = this.safeString (order, 'flag'); if (side === 'sale') { side = 'sell'; } // Can't use parseOrder because the data format is different btw endpoint for fetchOrder and fetchOrders return { 'info': order, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': undefined, 'side': side, 'price': this.safeFloat (order, 'price'), 'cost': undefined, 'average': this.safeFloat (order, 'avg_price'), 'amount': this.safeFloat (order, 'number'), 'filled': this.safeFloat (order, 'numberdeal'), 'remaining': this.safeFloat (order, 'numberover'), 'status': status, 'fee': undefined, 'clientOrderId': undefined, 'trades': undefined, }; } parseOrder (order, market = undefined) { const id = this.safeString (order, 'id'); const timestamp = this.safeTimestamp (order, 'datetime'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const type = undefined; let side = this.safeString (order, 'type'); if (side === 'sale') { side = 'sell'; } const price = this.safeFloat (order, 'price'); const average = this.safeFloat (order, 'avg_price'); const amount = this.safeFloat (order, 'amount'); const remaining = this.safeFloat (order, 'amount_outstanding'); const filled = amount - remaining; const status = this.parseOrderStatus (this.safeString (order, 'status')); const cost = filled * price; const fee = undefined; const result = { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; return result; } async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'type': type, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } const response = await this.privatePostApiOrderTradeList (this.extend (request, params)); if ('data' in response) { return this.parseOrders (response['data'], market, since, limit); } return []; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return this.fetchOrdersByType ('open', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'closed'); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return this.fetchOrdersByType ('all', symbol, since, limit, params); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), 'type': side, 'price': this.priceToPrecision (symbol, price), 'number': this.amountToPrecision (symbol, amount), }; const response = await this.privatePostApiOrderCoinTrust (this.extend (request, params)); const data = this.safeValue (response, 'data', {}); const id = this.safeString (data, 'order_id'); return { 'info': response, 'id': id, }; } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument'); } await this.loadMarkets (); const request = {}; if (symbol !== undefined) { request['symbol'] = this.marketId (symbol); } if (id !== undefined) { request['order_id'] = id; } return await this.privatePostApiOrderCancel (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); let payload = this.urlencode ({ 'api_key': this.apiKey }); if (Object.keys (query).length) { payload += '&' + this.urlencode (this.keysort (query)); } const auth = payload + '&secret_key=' + this.secret; const signature = this.hash (this.encode (auth)); body = payload + '&sign=' + signature; headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } const errorCode = this.safeValue (response, 'code'); if (errorCode !== undefined) { if (errorCode !== 0) { // // { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well) // { code: 1, msg: '公钥不合法' } - wrong public key // { code: 1, msg: '价格输入有误,请检查你的数值精度' } - 'The price input is incorrect, please check your numerical accuracy' // { code: 1, msg: '单笔最小交易数量不能小于0.00100000,请您重新挂单'} - // 'The minimum number of single transactions cannot be less than 0.00100000. Please re-post the order' // const message = this.safeString (response, 'msg'); const feedback = this.id + ' ' + message; this.throwExactlyMatchedException (this.exceptions, message, feedback); if (message.indexOf ('请您重新挂单') >= 0) { // minimum limit throw new InvalidOrder (feedback); } else { throw new ExchangeError (feedback); } } } } }; },{"./base/Exchange":7,"./base/errors":9}],22:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const hitbtc = require ('./hitbtc'); // --------------------------------------------------------------------------- module.exports = class bequant extends hitbtc { describe () { return this.deepExtend (super.describe (), { 'id': 'bequant', 'name': 'Bequant', 'countries': [ 'MT' ], // Malta 'pro': true, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/55248342-a75dfe00-525a-11e9-8aa2-05e9dca943c6.jpg', 'api': { 'public': 'https://api.bequant.io', 'private': 'https://api.bequant.io', }, 'www': 'https://bequant.io', 'doc': [ 'https://api.bequant.io/', ], 'fees': [ 'https://bequant.io/fees-and-limits', ], 'referral': 'https://bequant.io', }, }); } }; },{"./hitbtc":90}],23:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AccountSuspended, ArgumentsRequired, AuthenticationError, DDoSProtection, ExchangeNotAvailable, InvalidOrder, OrderNotFound, PermissionDenied, InsufficientFunds } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bibox extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bibox', 'name': 'Bibox', 'countries': [ 'CN', 'US', 'KR' ], 'version': 'v1', 'has': { 'CORS': false, 'publicAPI': false, 'fetchBalance': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchFundingFees': true, 'fetchTickers': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'createMarketOrder': false, // or they will return https://github.com/ccxt/ccxt/issues/2338 'withdraw': true, }, 'timeframes': { '1m': '1min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '2h': '2hour', '4h': '4hour', '6h': '6hour', '12h': '12hour', '1d': 'day', '1w': 'week', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/77257418-3262b000-6c85-11ea-8fb8-20bdf20b3592.jpg', 'api': 'https://api.bibox.com', 'www': 'https://www.bibox.com', 'doc': [ 'https://biboxcom.github.io/en/', ], 'fees': 'https://bibox.zendesk.com/hc/en-us/articles/360002336133', 'referral': 'https://w2.bibox.com/login/register?invite_code=05Kj3I', }, 'api': { 'public': { 'post': [ // TODO: rework for full endpoint/cmd paths here 'mdata', ], 'get': [ 'cquery', 'mdata', ], }, 'private': { 'post': [ 'cquery', 'ctrade', 'user', 'orderpending', 'transfer', ], }, 'v2private': { 'post': [ 'assets/transfer/spot', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.001, 'maker': 0.0008, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, 'exceptions': { '2011': AccountSuspended, // Account is locked '2015': AuthenticationError, // Google authenticator is wrong '2021': InsufficientFunds, // Insufficient balance available for withdrawal '2027': InsufficientFunds, // Insufficient balance available (for trade) '2033': OrderNotFound, // operation failed! Orders have been completed or revoked '2067': InvalidOrder, // Does not support market orders '2068': InvalidOrder, // The number of orders can not be less than '2085': InvalidOrder, // Order quantity is too small '3012': AuthenticationError, // invalid apiKey '3024': PermissionDenied, // wrong apikey permissions '3025': AuthenticationError, // signature failed '4000': ExchangeNotAvailable, // current network is unstable '4003': DDoSProtection, // server busy please try again later }, 'commonCurrencies': { 'KEY': 'Bihu', 'MTC': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025 'PAI': 'PCHAIN', }, }); } async fetchMarkets (params = {}) { const request = { 'cmd': 'marketAll', }; const response = await this.publicGetMdata (this.extend (request, params)); // // { // "result": [ // { // "is_hide":0, // "high_cny":"1.9478", // "amount":"272.41", // "coin_symbol":"BIX", // "last":"0.00002487", // "currency_symbol":"BTC", // "change":"+0.00000073", // "low_cny":"1.7408", // "base_last_cny":"1.84538041", // "area_id":7, // "percent":"+3.02%", // "last_cny":"1.8454", // "high":"0.00002625", // "low":"0.00002346", // "pair_type":0, // "last_usd":"0.2686", // "vol24H":"10940613", // "id":1, // "high_usd":"0.2835", // "low_usd":"0.2534" // } // ], // "cmd":"marketAll", // "ver":"1.1" // } // const markets = this.safeValue (response, 'result'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const numericId = this.safeInteger (market, 'id'); const baseId = this.safeString (market, 'coin_symbol'); const quoteId = this.safeString (market, 'currency_symbol'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const id = baseId + '_' + quoteId; const precision = { 'amount': 4, 'price': 8, }; result.push ({ 'id': id, 'numericId': numericId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'info': market, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, }, }); } return result; } parseTicker (ticker, market = undefined) { // we don't set values that are not defined by the exchange const timestamp = this.safeInteger (ticker, 'timestamp'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } else { const baseId = this.safeString (ticker, 'coin_symbol'); const quoteId = this.safeString (ticker, 'currency_symbol'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } const last = this.safeFloat (ticker, 'last'); const change = this.safeFloat (ticker, 'change'); const baseVolume = this.safeFloat2 (ticker, 'vol', 'vol24H'); let open = undefined; if ((last !== undefined) && (change !== undefined)) { open = last - change; } let percentage = this.safeString (ticker, 'percent'); if (percentage !== undefined) { percentage = percentage.replace ('%', ''); percentage = parseFloat (percentage); } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': this.safeFloat (ticker, 'amount'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'cmd': 'ticker', 'pair': market['id'], }; const response = await this.publicGetMdata (this.extend (request, params)); return this.parseTicker (response['result'], market); } parseTickers (rawTickers, symbols = undefined) { const tickers = []; for (let i = 0; i < rawTickers.length; i++) { const ticker = this.parseTicker (rawTickers[i]); if ((symbols === undefined) || (this.inArray (ticker['symbol'], symbols))) { tickers.push (ticker); } } return tickers; } async fetchTickers (symbols = undefined, params = {}) { const request = { 'cmd': 'marketAll', }; const response = await this.publicGetMdata (this.extend (request, params)); const tickers = this.parseTickers (response['result'], symbols); return this.indexBy (tickers, 'symbol'); } parseTrade (trade, market = undefined) { const timestamp = this.safeInteger2 (trade, 'time', 'createdAt'); let side = this.safeInteger2 (trade, 'side', 'order_side'); side = (side === 1) ? 'buy' : 'sell'; let symbol = undefined; if (market === undefined) { let marketId = this.safeString (trade, 'pair'); if (marketId === undefined) { const baseId = this.safeString (trade, 'coin_symbol'); const quoteId = this.safeString (trade, 'currency_symbol'); if ((baseId !== undefined) && (quoteId !== undefined)) { marketId = baseId + '_' + quoteId; } } if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); let feeCurrency = this.safeString (trade, 'fee_symbol'); if (feeCurrency !== undefined) { if (feeCurrency in this.currencies_by_id) { feeCurrency = this.currencies_by_id[feeCurrency]['code']; } else { feeCurrency = this.safeCurrencyCode (feeCurrency); } } const feeRate = undefined; // todo: deduce from market if market is defined const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined && amount !== undefined) { cost = price * amount; } if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': feeCurrency, 'rate': feeRate, }; } const id = this.safeString (trade, 'id'); return { 'info': trade, 'id': id, 'order': undefined, // Bibox does not have it (documented) yet 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': 'limit', 'takerOrMaker': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'cmd': 'deals', 'pair': market['id'], }; if (limit !== undefined) { request['size'] = limit; // default = 200 } const response = await this.publicGetMdata (this.extend (request, params)); return this.parseTrades (response['result'], market, since, limit); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'cmd': 'depth', 'pair': market['id'], }; if (limit !== undefined) { request['size'] = limit; // default = 200 } const response = await this.publicGetMdata (this.extend (request, params)); return this.parseOrderBook (response['result'], this.safeFloat (response['result'], 'update_time'), 'bids', 'asks', 'price', 'volume'); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv['time'], this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'vol'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 1000, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'cmd': 'kline', 'pair': market['id'], 'period': this.timeframes[timeframe], 'size': limit, }; const response = await this.publicGetMdata (this.extend (request, params)); return this.parseOHLCVs (response['result'], market, timeframe, since, limit); } async fetchCurrencies (params = {}) { if (!this.apiKey || !this.secret) { throw new AuthenticationError (this.id + " fetchCurrencies is an authenticated endpoint, therefore it requires 'apiKey' and 'secret' credentials. If you don't need currency details, set exchange.has['fetchCurrencies'] = false before calling its methods."); } const request = { 'cmd': 'transfer/coinList', 'body': {}, }; const response = await this.privatePostTransfer (this.extend (request, params)); const currencies = this.safeValue (response, 'result'); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'symbol'); const name = currency['name']; // contains hieroglyphs causing python ASCII bug const code = this.safeCurrencyCode (id); const precision = 8; const deposit = this.safeValue (currency, 'enable_deposit'); const withdraw = this.safeValue (currency, 'enable_withdraw'); const active = (deposit && withdraw); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': name, 'active': active, 'fee': undefined, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': Math.pow (10, precision), }, }, }; } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const type = this.safeString (params, 'type', 'assets'); params = this.omit (params, 'type'); const request = { 'cmd': 'transfer/' + type, // assets, mainAssets 'body': this.extend ({ 'select': 1, // return full info }, params), }; const response = await this.privatePostTransfer (request); const balances = this.safeValue (response, 'result'); const result = { 'info': balances }; let indexed = undefined; if ('assets_list' in balances) { indexed = this.indexBy (balances['assets_list'], 'coin_symbol'); } else { indexed = balances; } const keys = Object.keys (indexed); for (let i = 0; i < keys.length; i++) { const id = keys[i]; let code = id.toUpperCase (); if (code.indexOf ('TOTAL_') >= 0) { code = code.slice (6); } if (code in this.currencies_by_id) { code = this.currencies_by_id[code]['code']; } const account = this.account (); let balance = indexed[id]; if (typeof balance === 'string') { balance = parseFloat (balance); account['free'] = balance; account['used'] = 0.0; account['total'] = balance; } else { account['free'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'freeze'); } result[code] = account; } return this.parseBalance (result); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = { 'page': 1, }; if (code !== undefined) { currency = this.currency (code); request['symbol'] = currency['id']; } if (limit !== undefined) { request['size'] = limit; } else { request['size'] = 100; } const response = await this.privatePostTransfer ({ 'cmd': 'transfer/transferInList', 'body': this.extend (request, params), }); const deposits = this.safeValue (response['result'], 'items', []); for (let i = 0; i < deposits.length; i++) { deposits[i]['type'] = 'deposit'; } return this.parseTransactions (deposits, currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = { 'page': 1, }; if (code !== undefined) { currency = this.currency (code); request['symbol'] = currency['id']; } if (limit !== undefined) { request['size'] = limit; } else { request['size'] = 100; } const response = await this.privatePostTransfer ({ 'cmd': 'transfer/transferOutList', 'body': this.extend (request, params), }); const withdrawals = this.safeValue (response['result'], 'items', []); for (let i = 0; i < withdrawals.length; i++) { withdrawals[i]['type'] = 'withdrawal'; } return this.parseTransactions (withdrawals, currency, since, limit); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // 'id': 1023291, // 'coin_symbol': 'ETH', // 'to_address': '0x7263....', // 'amount': '0.49170000', // 'confirmCount': '16', // 'createdAt': 1553123867000, // 'status': 2 // } // // fetchWithdrawals // // { // 'id': 521844, // 'coin_symbol': 'ETH', // 'to_address': '0xfd4e....', // 'addr_remark': '', // 'amount': '0.39452750', // 'fee': '0.00600000', // 'createdAt': 1553226906000, // 'memo': '', // 'status': 3 // } // const id = this.safeString (transaction, 'id'); const address = this.safeString (transaction, 'to_address'); const currencyId = this.safeString (transaction, 'coin_symbol'); const code = this.safeCurrencyCode (currencyId, currency); const timestamp = this.safeString (transaction, 'createdAt'); let tag = this.safeString (transaction, 'addr_remark'); const type = this.safeString (transaction, 'type'); const status = this.parseTransactionStatusByType (this.safeString (transaction, 'status'), type); const amount = this.safeFloat (transaction, 'amount'); let feeCost = this.safeFloat (transaction, 'fee'); if (type === 'deposit') { feeCost = 0; tag = undefined; } const fee = { 'cost': feeCost, 'currency': code, }; return { 'info': transaction, 'id': id, 'txid': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': fee, }; } parseTransactionStatusByType (status, type = undefined) { const statuses = { 'deposit': { '1': 'pending', '2': 'ok', }, 'withdrawal': { '0': 'pending', '3': 'ok', }, }; return this.safeString (this.safeValue (statuses, type, {}), status, status); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const orderType = (type === 'limit') ? 2 : 1; const orderSide = (side === 'buy') ? 1 : 2; const request = { 'cmd': 'orderpending/trade', 'body': this.extend ({ 'pair': market['id'], 'account_type': 0, 'order_type': orderType, 'order_side': orderSide, 'pay_bix': 0, 'amount': amount, 'price': price, }, params), }; const response = await this.privatePostOrderpending (request); return { 'info': response, 'id': this.safeString (response, 'result'), }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'cmd': 'orderpending/cancelTrade', 'body': this.extend ({ 'orders_id': id, }, params), }; const response = await this.privatePostOrderpending (request); return response; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'cmd': 'orderpending/order', 'body': this.extend ({ 'id': id.toString (), 'account_type': 0, // 0 = spot account }, params), }; const response = await this.privatePostOrderpending (request); const order = this.safeValue (response, 'result'); if (this.isEmpty (order)) { throw new OrderNotFound (this.id + ' order ' + id + ' not found'); } return this.parseOrder (order); } parseOrder (order, market = undefined) { let symbol = undefined; if (market === undefined) { let marketId = undefined; const baseId = this.safeString (order, 'coin_symbol'); const quoteId = this.safeString (order, 'currency_symbol'); if ((baseId !== undefined) && (quoteId !== undefined)) { marketId = baseId + '_' + quoteId; } if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } const type = (order['order_type'] === 1) ? 'market' : 'limit'; const timestamp = order['createdAt']; const price = this.safeFloat (order, 'price'); const average = this.safeFloat (order, 'deal_price'); const filled = this.safeFloat (order, 'deal_amount'); const amount = this.safeFloat (order, 'amount'); let cost = this.safeFloat2 (order, 'deal_money', 'money'); let remaining = undefined; if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; } if (cost === undefined) { cost = price * filled; } } const side = (order['order_side'] === 1) ? 'buy' : 'sell'; const status = this.parseOrderStatus (this.safeString (order, 'status')); const id = this.safeString (order, 'id'); const feeCost = this.safeFloat (order, 'fee'); let fee = undefined; if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': undefined, }; } cost = cost ? cost : (parseFloat (price) * filled); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } parseOrderStatus (status) { const statuses = { // original comments from bibox: '1': 'open', // pending '2': 'open', // part completed '3': 'closed', // completed '4': 'canceled', // part canceled '5': 'canceled', // canceled '6': 'canceled', // canceling }; return this.safeString (statuses, status, status); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; let pair = undefined; if (symbol !== undefined) { market = this.market (symbol); pair = market['id']; } const size = limit ? limit : 200; const request = { 'cmd': 'orderpending/orderPendingList', 'body': this.extend ({ 'pair': pair, 'account_type': 0, // 0 - regular, 1 - margin 'page': 1, 'size': size, }, params), }; const response = await this.privatePostOrderpending (request); const orders = this.safeValue (response['result'], 'items', []); return this.parseOrders (orders, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = 200, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a `symbol` argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'cmd': 'orderpending/pendingHistoryList', 'body': this.extend ({ 'pair': market['id'], 'account_type': 0, // 0 - regular, 1 - margin 'page': 1, 'size': limit, }, params), }; const response = await this.privatePostOrderpending (request); const orders = this.safeValue (response['result'], 'items', []); return this.parseOrders (orders, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); } await this.loadMarkets (); const market = this.market (symbol); const size = limit ? limit : 200; const request = { 'cmd': 'orderpending/orderHistoryList', 'body': this.extend ({ 'pair': market['id'], 'account_type': 0, // 0 - regular, 1 - margin 'page': 1, 'size': size, 'coin_symbol': market['baseId'], 'currency_symbol': market['quoteId'], }, params), }; const response = await this.privatePostOrderpending (request); const trades = this.safeValue (response['result'], 'items', []); return this.parseTrades (trades, market, since, limit); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'cmd': 'transfer/transferIn', 'body': this.extend ({ 'coin_symbol': currency['id'], }, params), }; const response = await this.privatePostTransfer (request); // // { // "result":"3Jx6RZ9TNMsAoy9NUzBwZf68QBppDruSKW","cmd":"transfer/transferIn" // } // // { // "result":"{\"account\":\"PERSONALLY OMITTED\",\"memo\":\"PERSONALLY OMITTED\"}","cmd":"transfer/transferIn" // } // const result = this.safeString (response, 'result'); let address = result; let tag = undefined; if (this.isJsonEncodedObject (result)) { const parsed = JSON.parse (result); address = this.safeString (parsed, 'account'); tag = this.safeString (parsed, 'memo'); } return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); if (this.password === undefined) { if (!('trade_pwd' in params)) { throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a trade_pwd parameter'); } } if (!('totp_code' in params)) { throw new ExchangeError (this.id + ' withdraw() requires a totp_code parameter for 2FA authentication'); } const request = { 'trade_pwd': this.password, 'coin_symbol': currency['id'], 'amount': amount, 'addr': address, }; if (tag !== undefined) { request['address_remark'] = tag; } const response = await this.privatePostTransfer ({ 'cmd': 'transfer/transferOut', 'body': this.extend (request, params), }); return { 'info': response, 'id': undefined, }; } async fetchFundingFees (codes = undefined, params = {}) { // by default it will try load withdrawal fees of all currencies (with separate requests) // however if you define codes = [ 'ETH', 'BTC' ] in args it will only load those await this.loadMarkets (); const withdrawFees = {}; const info = {}; if (codes === undefined) { codes = Object.keys (this.currencies); } for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currency = this.currency (code); const request = { 'cmd': 'transfer/coinConfig', 'body': this.extend ({ 'coin_symbol': currency['id'], }, params), }; const response = await this.privatePostTransfer (request); info[code] = response; withdrawFees[code] = this.safeFloat (response['result'], 'withdraw_fee'); } return { 'info': info, 'withdraw': withdrawFees, 'deposit': {}, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + path; const cmds = this.json ([ params ]); if (api === 'public') { if (method !== 'GET') { body = { 'cmds': cmds }; } else if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else if (api === 'v2private') { this.checkRequiredCredentials (); url = this.urls['api'] + '/v2/' + path; const json_params = this.json (params); body = { 'body': json_params, 'apikey': this.apiKey, 'sign': this.hmac (this.encode (json_params), this.encode (this.secret), 'md5'), }; } else { this.checkRequiredCredentials (); body = { 'cmds': cmds, 'apikey': this.apiKey, 'sign': this.hmac (this.encode (cmds), this.encode (this.secret), 'md5'), }; } if (body !== undefined) { body = this.json (body, { 'convertArraysToObjects': true }); } headers = { 'Content-Type': 'application/json' }; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } if ('error' in response) { if ('code' in response['error']) { const code = this.safeString (response['error'], 'code'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, code, feedback); throw new ExchangeError (feedback); } throw new ExchangeError (this.id + ' ' + body); } if (!('result' in response)) { throw new ExchangeError (this.id + ' ' + body); } } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if (method === 'GET') { return response; } else { return response['result'][0]; } } }; },{"./base/Exchange":7,"./base/errors":9}],24:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, AuthenticationError, OrderNotFound, InsufficientFunds, PermissionDenied, BadRequest, RateLimitExceeded, InvalidOrder } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bigone extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bigone', 'name': 'BigONE', 'countries': [ 'CN' ], 'version': 'v3', 'rateLimit': 1200, // 500 request per 10 minutes 'has': { 'cancelAllOrders': true, 'createMarketOrder': false, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchTickers': true, 'fetchWithdrawals': true, 'withdraw': true, }, 'timeframes': { '1m': 'min1', '5m': 'min5', '15m': 'min15', '30m': 'min30', '1h': 'hour1', '3h': 'hour3', '4h': 'hour4', '6h': 'hour6', '12h': 'hour12', '1d': 'day1', '1w': 'week1', '1M': 'month1', }, 'hostname': 'big.one', // or 'bigone.com' 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/69354403-1d532180-0c91-11ea-88ed-44c06cefdf87.jpg', 'api': { 'public': 'https://{hostname}/api/v3', 'private': 'https://{hostname}/api/v3/viewer', }, 'www': 'https://big.one', 'doc': 'https://open.big.one/docs/api.html', 'fees': 'https://bigone.zendesk.com/hc/en-us/articles/115001933374-BigONE-Fee-Policy', 'referral': 'https://b1.run/users/new?code=D3LLBVFT', }, 'api': { 'public': { 'get': [ 'ping', 'asset_pairs', 'asset_pairs/{asset_pair_name}/depth', 'asset_pairs/{asset_pair_name}/trades', 'asset_pairs/{asset_pair_name}/ticker', 'asset_pairs/{asset_pair_name}/candles', 'asset_pairs/tickers', ], }, 'private': { 'get': [ 'accounts', 'assets/{asset_symbol}/address', 'orders', 'orders/{id}', 'orders/multi', 'trades', 'withdrawals', 'deposits', ], 'post': [ 'orders', 'orders/{id}/cancel', 'orders/cancel', 'withdrawals', ], }, }, 'fees': { 'trading': { 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, 'funding': { // HARDCODING IS DEPRECATED THE FEES BELOW ARE TO BE REMOVED SOON 'withdraw': { 'BTC': 0.001, 'ETH': 0.005, 'EOS': 0.01, 'ZEC': 0.003, 'LTC': 0.01, 'QTUM': 0.01, // 'INK': 0.01 QTUM, // 'BOT': 0.01 QTUM, 'ETC': 0.01, 'GAS': 0.0, 'BTS': 1.0, 'GXS': 0.1, 'BITCNY': 19.0, }, }, }, 'exceptions': { 'exact': { '10001': BadRequest, // syntax error '10005': ExchangeError, // internal error "Amount's scale must greater than AssetPair's base scale": InvalidOrder, '10007': BadRequest, // parameter error, {"code":10007,"message":"Amount's scale must greater than AssetPair's base scale"} '10011': ExchangeError, // system error '10013': OrderNotFound, // {"code":10013,"message":"Resource not found"} '10014': InsufficientFunds, // {"code":10014,"message":"Insufficient funds"} '10403': PermissionDenied, // permission denied '10429': RateLimitExceeded, // too many requests '40004': AuthenticationError, // {"code":40004,"message":"invalid jwt"} '40103': AuthenticationError, // invalid otp code '40104': AuthenticationError, // invalid asset pin code '40301': PermissionDenied, // {"code":40301,"message":"Permission denied withdrawal create"} '40302': ExchangeError, // already requested '40601': ExchangeError, // resource is locked '40602': ExchangeError, // resource is depleted '40603': InsufficientFunds, // insufficient resource '40120': InvalidOrder, // Order is in trading '40121': InvalidOrder, // Order is already cancelled or filled }, 'broad': { }, }, 'commonCurrencies': { 'ONE': 'BigONE Token', }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetAssetPairs (params); // // { // "code":0, // "data":[ // { // "id":"01e48809-b42f-4a38-96b1-c4c547365db1", // "name":"PCX-BTC", // "quote_scale":7, // "quote_asset":{ // "id":"0df9c3c3-255a-46d7-ab82-dedae169fba9", // "symbol":"BTC", // "name":"Bitcoin", // }, // "base_asset":{ // "id":"405484f7-4b03-4378-a9c1-2bd718ecab51", // "symbol":"PCX", // "name":"ChainX", // }, // "base_scale":3, // "min_quote_value":"0.0001", // }, // ] // } // const markets = this.safeValue (response, 'data', []); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'name'); const uuid = this.safeString (market, 'id'); const baseAsset = this.safeValue (market, 'base_asset', {}); const quoteAsset = this.safeValue (market, 'quote_asset', {}); const baseId = this.safeString (baseAsset, 'symbol'); const quoteId = this.safeString (quoteAsset, 'symbol'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'base_scale'), 'price': this.safeInteger (market, 'quote_scale'), }; const minCost = this.safeInteger (market, 'min_quote_value'); const entry = { 'id': id, 'uuid': uuid, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': minCost, 'max': undefined, }, }, 'info': market, }; result.push (entry); } return result; } async loadMarkets (reload = false, params = {}) { const markets = await super.loadMarkets (reload, params); let marketsByUuid = this.safeValue (this.options, 'marketsByUuid'); if ((marketsByUuid === undefined) || reload) { marketsByUuid = {}; for (let i = 0; i < this.symbols.length; i++) { const symbol = this.symbols[i]; const market = this.markets[symbol]; const uuid = this.safeString (market, 'uuid'); marketsByUuid[uuid] = market; } this.options['marketsByUuid'] = marketsByUuid; } return markets; } parseTicker (ticker, market = undefined) { // // { // "asset_pair_name":"ETH-BTC", // "bid":{"price":"0.021593","order_count":1,"quantity":"0.20936"}, // "ask":{"price":"0.021613","order_count":1,"quantity":"2.87064"}, // "open":"0.021795", // "high":"0.021795", // "low":"0.021471", // "close":"0.021613", // "volume":"117078.90431", // "daily_change":"-0.000182" // } // let symbol = undefined; if (market === undefined) { const marketId = this.safeString (ticker, 'asset_pair_name'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.milliseconds (); const close = this.safeFloat (ticker, 'close'); const bid = this.safeValue (ticker, 'bid', {}); const ask = this.safeValue (ticker, 'ask', {}); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (bid, 'price'), 'bidVolume': this.safeFloat (bid, 'quantity'), 'ask': this.safeFloat (ask, 'price'), 'askVolume': this.safeFloat (ask, 'quantity'), 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': close, 'last': close, 'previousClose': undefined, 'change': this.safeFloat (ticker, 'daily_change'), 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'asset_pair_name': market['id'], }; const response = await this.publicGetAssetPairsAssetPairNameTicker (this.extend (request, params)); // // { // "code":0, // "data":{ // "asset_pair_name":"ETH-BTC", // "bid":{"price":"0.021593","order_count":1,"quantity":"0.20936"}, // "ask":{"price":"0.021613","order_count":1,"quantity":"2.87064"}, // "open":"0.021795", // "high":"0.021795", // "low":"0.021471", // "close":"0.021613", // "volume":"117078.90431", // "daily_change":"-0.000182" // } // } // const ticker = this.safeValue (response, 'data', {}); return this.parseTicker (ticker, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbols !== undefined) { const ids = this.marketIds (symbols); request['pair_names'] = ids.join (','); } const response = await this.publicGetAssetPairsTickers (this.extend (request, params)); // // { // "code":0, // "data":[ // { // "asset_pair_name":"PCX-BTC", // "bid":{"price":"0.000234","order_count":1,"quantity":"0.518"}, // "ask":{"price":"0.0002348","order_count":1,"quantity":"2.348"}, // "open":"0.0002343", // "high":"0.0002348", // "low":"0.0002162", // "close":"0.0002348", // "volume":"12887.016", // "daily_change":"0.0000005" // }, // { // "asset_pair_name":"GXC-USDT", // "bid":{"price":"0.5054","order_count":1,"quantity":"40.53"}, // "ask":{"price":"0.5055","order_count":1,"quantity":"38.53"}, // "open":"0.5262", // "high":"0.5323", // "low":"0.5055", // "close":"0.5055", // "volume":"603963.05", // "daily_change":"-0.0207" // } // ] // } // const tickers = this.safeValue (response, 'data', []); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = this.parseTicker (tickers[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'asset_pair_name': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 50, max 200 } const response = await this.publicGetAssetPairsAssetPairNameDepth (this.extend (request, params)); // // { // "code":0, // "data": { // "asset_pair_name": "EOS-BTC", // "bids": [ // { "price": "42", "order_count": 4, "quantity": "23.33363711" } // ], // "asks": [ // { "price": "45", "order_count": 2, "quantity": "4193.3283464" } // ] // } // } // const orderbook = this.safeValue (response, 'data', {}); return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'quantity'); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "id": 38199941, // "price": "3378.67", // "amount": "0.019812", // "taker_side": "ASK", // "created_at": "2019-01-29T06:05:56Z" // } // // fetchMyTrades (private) // // { // "id": 10854280, // "asset_pair_name": "XIN-USDT", // "price": "70", // "amount": "1", // "taker_side": "ASK", // "maker_order_id": 58284908, // "taker_order_id": 58284909, // "maker_fee": "0.0008", // "taker_fee": "0.07", // "side": "SELF_TRADING", // "inserted_at": "2019-04-16T12:00:01Z" // }, // // { // "id": 10854263, // "asset_pair_name": "XIN-USDT", // "price": "75.7", // "amount": "12.743149", // "taker_side": "BID", // "maker_order_id": null, // "taker_order_id": 58284888, // "maker_fee": null, // "taker_fee": "0.0025486298", // "side": "BID", // "inserted_at": "2019-04-15T06:20:57Z" // } // const timestamp = this.parse8601 (this.safeString2 (trade, 'created_at', 'inserted_at')); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); const marketId = this.safeString (trade, 'asset_pair_name'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = this.costToPrecision (symbol, price * amount); } } let side = this.safeString (trade, 'side'); const takerSide = this.safeString (trade, 'taker_side'); let takerOrMaker = undefined; if ((takerSide !== undefined) && (side !== undefined) && (side !== 'SELF_TRADING')) { takerOrMaker = (takerSide === side) ? 'taker' : 'maker'; } if (side === undefined) { // taker side is not related to buy/sell side // the following code is probably a mistake side = (takerSide === 'ASK') ? 'sell' : 'buy'; } else { if (side === 'BID') { side = 'buy'; } else if (side === 'ASK') { side = 'sell'; } } const makerOrderId = this.safeString (trade, 'maker_order_id'); const takerOrderId = this.safeString (trade, 'taker_order_id'); let orderId = undefined; if (makerOrderId !== undefined) { if (takerOrderId !== undefined) { orderId = [ makerOrderId, takerOrderId ]; } else { orderId = makerOrderId; } } else if (takerOrderId !== undefined) { orderId = takerOrderId; } const id = this.safeString (trade, 'id'); const result = { 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': 'limit', 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': parseFloat (cost), 'info': trade, }; let makerCurrencyCode = undefined; let takerCurrencyCode = undefined; if ((market !== undefined) && (takerOrMaker !== undefined)) { if (side === 'buy') { if (takerOrMaker === 'maker') { makerCurrencyCode = market['base']; takerCurrencyCode = market['quote']; } else { makerCurrencyCode = market['quote']; takerCurrencyCode = market['base']; } } else { if (takerOrMaker === 'maker') { makerCurrencyCode = market['quote']; takerCurrencyCode = market['base']; } else { makerCurrencyCode = market['base']; takerCurrencyCode = market['quote']; } } } else if (side === 'SELF_TRADING') { if (takerSide === 'BID') { makerCurrencyCode = market['quote']; takerCurrencyCode = market['base']; } else if (takerSide === 'ASK') { makerCurrencyCode = market['base']; takerCurrencyCode = market['quote']; } } const makerFeeCost = this.safeFloat (trade, 'maker_fee'); const takerFeeCost = this.safeFloat (trade, 'taker_fee'); if (makerFeeCost !== undefined) { if (takerFeeCost !== undefined) { result['fees'] = [ { 'cost': makerFeeCost, 'currency': makerCurrencyCode }, { 'cost': takerFeeCost, 'currency': takerCurrencyCode }, ]; } else { result['fee'] = { 'cost': makerFeeCost, 'currency': makerCurrencyCode }; } } else if (takerFeeCost !== undefined) { result['fee'] = { 'cost': takerFeeCost, 'currency': takerCurrencyCode }; } else { result['fee'] = undefined; } return result; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'asset_pair_name': market['id'], }; const response = await this.publicGetAssetPairsAssetPairNameTrades (this.extend (request, params)); // // { // "code": 0, // "data": [ // { // "id": 38199941, // "price": "3378.67", // "amount": "0.019812", // "taker_side": "ASK", // "created_at": "2019-01-29T06:05:56Z" // }, // { // "id": 38199934, // "price": "3376.14", // "amount": "0.019384", // "taker_side": "ASK", // "created_at": "2019-01-29T06:05:40Z" // } // ] // } // const trades = this.safeValue (response, 'data', []); return this.parseTrades (trades, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // { // close: '0.021562', // high: '0.021563', // low: '0.02156', // open: '0.021563', // time: '2019-11-21T07:54:00Z', // volume: '59.84376' // } // return [ this.parse8601 (this.safeString (ohlcv, 'time')), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 100; // default 100, max 500 } const request = { 'asset_pair_name': market['id'], 'period': this.timeframes[timeframe], 'limit': limit, }; if (since !== undefined) { // const start = parseInt (since / 1000); const end = this.sum (since, limit * this.parseTimeframe (timeframe) * 1000); request['time'] = this.iso8601 (end); } const response = await this.publicGetAssetPairsAssetPairNameCandles (this.extend (request, params)); // // { // code: 0, // data: [ // { // close: '0.021656', // high: '0.021658', // low: '0.021652', // open: '0.021652', // time: '2019-11-21T09:30:00Z', // volume: '53.08664' // }, // { // close: '0.021652', // high: '0.021656', // low: '0.021652', // open: '0.021656', // time: '2019-11-21T09:29:00Z', // volume: '88.39861' // }, // ] // } // const ohlcvs = this.safeValue (response, 'data', []); return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccounts (params); // // { // "code":0, // "data":[ // {"asset_symbol":"NKC","balance":"0","locked_balance":"0"}, // {"asset_symbol":"UBTC","balance":"0","locked_balance":"0"}, // {"asset_symbol":"READ","balance":"0","locked_balance":"0"}, // ], // } // const result = { 'info': response }; const balances = this.safeValue (response, 'data', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const symbol = this.safeString (balance, 'asset_symbol'); const code = this.safeCurrencyCode (symbol); const account = this.account (); account['total'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'locked_balance'); result[code] = account; } return this.parseBalance (result); } parseOrder (order, market = undefined) { // // { // "id": 10, // "asset_pair_name": "EOS-BTC", // "price": "10.00", // "amount": "10.00", // "filled_amount": "9.0", // "avg_deal_price": "12.0", // "side": "ASK", // "state": "FILLED", // "created_at":"2019-01-29T06:05:56Z", // "updated_at":"2019-01-29T06:05:56Z", // } // const id = this.safeString (order, 'id'); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'asset_pair_name'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'filled_amount'); let remaining = undefined; if (amount !== undefined && filled !== undefined) { remaining = Math.max (0, amount - filled); } const status = this.parseOrderStatus (this.safeString (order, 'state')); let side = this.safeString (order, 'side'); if (side === 'BID') { side = 'buy'; } else { side = 'sell'; } let cost = undefined; if (filled !== undefined) { if (price !== undefined) { cost = filled * price; } } const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'updated_at')); const average = this.safeFloat (order, 'avg_deal_price'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); side = (side === 'buy') ? 'BID' : 'ASK'; const request = { 'asset_pair_name': market['id'], // asset pair name BTC-USDT, required 'side': side, // order side one of "ASK"/"BID", required 'amount': this.amountToPrecision (symbol, amount), // order amount, string, required 'price': this.priceToPrecision (symbol, price), // order price, string, required }; const response = await this.privatePostOrders (this.extend (request, params)); // // { // "id": 10, // "asset_pair_name": "EOS-BTC", // "price": "10.00", // "amount": "10.00", // "filled_amount": "9.0", // "avg_deal_price": "12.0", // "side": "ASK", // "state": "FILLED", // "created_at":"2019-01-29T06:05:56Z", // "updated_at":"2019-01-29T06:05:56Z" // } // const order = this.safeValue (response, 'data'); return this.parseOrder (order, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id }; const response = await this.privatePostOrdersIdCancel (this.extend (request, params)); // { // "id": 10, // "asset_pair_name": "EOS-BTC", // "price": "10.00", // "amount": "10.00", // "filled_amount": "9.0", // "avg_deal_price": "12.0", // "side": "ASK", // "state": "CANCELLED", // "created_at":"2019-01-29T06:05:56Z", // "updated_at":"2019-01-29T06:05:56Z" // } const order = this.safeValue (response, 'data'); return this.parseOrder (order); } async cancelAllOrders (symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'asset_pair_name': market['id'], }; const response = await this.privatePostOrdersCancel (this.extend (request, params)); // // { // "code":0, // "data": { // "cancelled":[ // 58272370, // 58272377 // ], // "failed": [] // } // } // return response; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id }; const response = await this.privateGetOrdersId (this.extend (request, params)); const order = this.safeValue (response, 'data', {}); return this.parseOrder (order); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'asset_pair_name': market['id'], // 'page_token': 'dxzef', // request page after this page token // 'side': 'ASK', // 'ASK' or 'BID', optional // 'state': 'FILLED', // 'CANCELLED', 'FILLED', 'PENDING' // 'limit' 20, // default 20, max 200 }; if (limit !== undefined) { request['limit'] = limit; // default 20, max 200 } const response = await this.privateGetOrders (this.extend (request, params)); // // { // "code":0, // "data": [ // { // "id": 10, // "asset_pair_name": "ETH-BTC", // "price": "10.00", // "amount": "10.00", // "filled_amount": "9.0", // "avg_deal_price": "12.0", // "side": "ASK", // "state": "FILLED", // "created_at":"2019-01-29T06:05:56Z", // "updated_at":"2019-01-29T06:05:56Z", // }, // ], // "page_token":"dxzef", // } // const orders = this.safeValue (response, 'data', []); return this.parseOrders (orders, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument'); } const market = this.market (symbol); const request = { 'asset_pair_name': market['id'], // 'page_token': 'dxzef', // request page after this page token }; if (limit !== undefined) { request['limit'] = limit; // default 20, max 200 } const response = await this.privateGetTrades (this.extend (request, params)); // // { // "code": 0, // "data": [ // { // "id": 10854280, // "asset_pair_name": "XIN-USDT", // "price": "70", // "amount": "1", // "taker_side": "ASK", // "maker_order_id": 58284908, // "taker_order_id": 58284909, // "maker_fee": "0.0008", // "taker_fee": "0.07", // "side": "SELF_TRADING", // "inserted_at": "2019-04-16T12:00:01Z" // }, // { // "id": 10854263, // "asset_pair_name": "XIN-USDT", // "price": "75.7", // "amount": "12.743149", // "taker_side": "BID", // "maker_order_id": null, // "taker_order_id": 58284888, // "maker_fee": null, // "taker_fee": "0.0025486298", // "side": "BID", // "inserted_at": "2019-04-15T06:20:57Z" // } // ], // "page_token":"dxfv" // } // const trades = this.safeValue (response, 'data', []); return this.parseTrades (trades, market, since, limit); } parseOrderStatus (status) { const statuses = { 'PENDING': 'open', 'FILLED': 'closed', 'CANCELLED': 'canceled', }; return this.safeString (statuses, status); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'state': 'PENDING', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'state': 'FILLED', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } nonce () { return this.microseconds () * 1000; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const query = this.omit (params, this.extractParams (path)); const baseUrl = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname }); let url = baseUrl + '/' + this.implodeParams (path, params); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const request = { 'type': 'OpenAPIV2', 'sub': this.apiKey, 'nonce': nonce, // 'recv_window': '30', // default 30 }; const jwt = this.jwt (request, this.encode (this.secret)); headers = { 'Authorization': 'Bearer ' + jwt, }; if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (method === 'POST') { headers['Content-Type'] = 'application/json'; body = this.json (query); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset_symbol': currency['id'], }; const response = await this.privateGetAssetsAssetSymbolAddress (this.extend (request, params)); // // the actual response format is not the same as the documented one // the data key contains an array in the actual response // // { // "code":0, // "message":"", // "data":[ // { // "id":5521878, // "chain":"Bitcoin", // "value":"1GbmyKoikhpiQVZ1C9sbF17mTyvBjeobVe", // "memo":"" // } // ] // } // const data = this.safeValue (response, 'data', []); const dataLength = data.length; if (dataLength < 1) { throw new ExchangeError (this.id + 'fetchDepositAddress() returned empty address response'); } const firstElement = data[0]; const address = this.safeString (firstElement, 'value'); const tag = this.safeString (firstElement, 'memo'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } parseTransactionStatus (status) { const statuses = { // what are other statuses here? 'WITHHOLD': 'ok', // deposits 'UNCONFIRMED': 'pending', 'CONFIRMED': 'ok', // withdrawals }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // "amount": "25.0", // "asset_symbol": "BTS" // "confirms": 100, // "id": 5, // "inserted_at": "2018-02-16T11:39:58.000Z", // "is_internal": false, // "kind": "default", // "memo": "", // "state": "WITHHOLD", // "txid": "72e03037d144dae3d32b68b5045462b1049a0755", // "updated_at": "2018-11-09T10:20:09.000Z", // } // // fetchWithdrawals // // { // "amount": "5", // "asset_symbol": "ETH", // "completed_at": "2018-03-15T16:13:45.610463Z", // "customer_id": "10", // "id": 10, // "inserted_at": "2018-03-15T16:13:45.610463Z", // "is_internal": true, // "note": "2018-03-15T16:13:45.610463Z", // "state": "CONFIRMED", // "target_address": "0x4643bb6b393ac20a6175c713175734a72517c63d6f7" // "txid": "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0", // } // const currencyId = this.safeString (transaction, 'asset_symbol'); const code = this.safeCurrencyCode (currencyId); const id = this.safeInteger (transaction, 'id'); const amount = this.safeFloat (transaction, 'amount'); const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); const timestamp = this.parse8601 (this.safeString (transaction, 'inserted_at')); const updated = this.parse8601 (this.safeString2 (transaction, 'updated_at', 'completed_at')); const txid = this.safeString (transaction, 'txid'); const address = this.safeString (transaction, 'target_address'); const tag = this.safeString (transaction, 'memo'); const type = ('customer_id' in transaction) ? 'deposit' : 'withdrawal'; return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'addressFrom': undefined, 'address': undefined, 'addressTo': address, 'tagFrom': undefined, 'tag': tag, 'tagTo': undefined, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': updated, 'fee': undefined, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'page_token': 'dxzef', // request page after this page token // 'limit': 50, // optional, default 50 // 'kind': 'string', // optional - air_drop, big_holder_dividend, default, eosc_to_eos, internal, equally_airdrop, referral_mining, one_holder_dividend, single_customer, snapshotted_airdrop, trade_mining // 'asset_symbol': 'BTC', // optional }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['asset_symbol'] = currency['id']; } if (limit !== undefined) { request['limit'] = limit; // default 50 } const response = await this.privateGetDeposits (this.extend (request, params)); // // { // "code": 0, // "page_token": "NQ==", // "data": [ // { // "id": 5, // "amount": "25.0", // "confirms": 100, // "txid": "72e03037d144dae3d32b68b5045462b1049a0755", // "is_internal": false, // "inserted_at": "2018-02-16T11:39:58.000Z", // "updated_at": "2018-11-09T10:20:09.000Z", // "kind": "default", // "memo": "", // "state": "WITHHOLD", // "asset_symbol": "BTS" // } // ] // } // const deposits = this.safeValue (response, 'data', []); return this.parseTransactions (deposits, code, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'page_token': 'dxzef', // request page after this page token // 'limit': 50, // optional, default 50 // 'kind': 'string', // optional - air_drop, big_holder_dividend, default, eosc_to_eos, internal, equally_airdrop, referral_mining, one_holder_dividend, single_customer, snapshotted_airdrop, trade_mining // 'asset_symbol': 'BTC', // optional }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['asset_symbol'] = currency['id']; } if (limit !== undefined) { request['limit'] = limit; // default 50 } const response = await this.privateGetWithdrawals (this.extend (request, params)); // // { // "code": 0, // "data": [ // { // "id": 10, // "customer_id": "10", // "asset_symbol": "ETH", // "amount": "5", // "state": "CONFIRMED", // "note": "2018-03-15T16:13:45.610463Z", // "txid": "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0", // "completed_at": "2018-03-15T16:13:45.610463Z", // "inserted_at": "2018-03-15T16:13:45.610463Z", // "is_internal": true, // "target_address": "0x4643bb6b393ac20a6175c713175734a72517c63d6f7" // } // ], // "page_token":"dxvf" // } // const withdrawals = this.safeValue (response, 'data', []); return this.parseTransactions (withdrawals, code, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'symbol': currency['id'], 'target_address': address, 'amount': this.currencyToPrecision (code, amount), }; if (tag !== undefined) { request['memo'] = tag; } // requires write permission on the wallet const response = await this.privatePostWithdrawals (this.extend (request, params)); // // { // "code":0, // "message":"", // "data":[ // { // "id":1, // "customer_id":7, // "asset_uuid":"50293b12-5be8-4f5b-b31d-d43cdd5ccc29", // "amount":"100", // "recipient":null, // "state":"PENDING", // "is_internal":true, // "note":"asdsadsad", // "kind":"on_chain", // "txid":"asdasdasdsadsadsad", // "confirms":5, // "inserted_at":null, // "updated_at":null, // "completed_at":null, // "commision":null, // "explain":"" // } // ] // } // const data = this.safeValue (response, 'data', []); const dataLength = data.length; if (dataLength < 1) { throw new ExchangeError (this.id + ' withdraw() returned an empty response'); } const transaction = data[0]; return this.parseTransaction (transaction, currency); } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // // {"code":10013,"message":"Resource not found"} // {"code":40004,"message":"invalid jwt"} // const code = this.safeString (response, 'code'); const message = this.safeString (response, 'message'); if (code !== '0') { const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],25:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress, RateLimitExceeded, PermissionDenied, NotSupported } = require ('./base/errors'); const { ROUND, TRUNCATE } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class binance extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'binance', 'name': 'Binance', 'countries': [ 'JP', 'MT' ], // Japan, Malta 'rateLimit': 500, 'certified': true, 'pro': true, // new metainfo interface 'has': { 'fetchDepositAddress': true, 'CORS': false, 'fetchBidsAsks': true, 'fetchTickers': true, 'fetchTime': true, 'fetchOHLCV': true, 'fetchMyTrades': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': 'emulated', 'withdraw': true, 'fetchFundingFees': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTransactions': false, 'fetchTradingFee': true, 'fetchTradingFees': true, 'cancelAllOrders': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '8h': '8h', '12h': '12h', '1d': '1d', '3d': '3d', '1w': '1w', '1M': '1M', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg', 'test': { 'fapiPublic': 'https://testnet.binancefuture.com/fapi/v1', 'fapiPrivate': 'https://testnet.binancefuture.com/fapi/v1', }, 'api': { 'web': 'https://www.binance.com', 'wapi': 'https://api.binance.com/wapi/v3', 'sapi': 'https://api.binance.com/sapi/v1', 'fapiPublic': 'https://fapi.binance.com/fapi/v1', 'fapiPrivate': 'https://fapi.binance.com/fapi/v1', 'public': 'https://api.binance.com/api/v3', 'private': 'https://api.binance.com/api/v3', 'v3': 'https://api.binance.com/api/v3', 'v1': 'https://api.binance.com/api/v1', }, 'www': 'https://www.binance.com', 'referral': 'https://www.binance.com/?ref=10205187', 'doc': [ 'https://binance-docs.github.io/apidocs/spot/en', ], 'api_management': 'https://www.binance.com/en/usercenter/settings/api-management', 'fees': 'https://www.binance.com/en/fee/schedule', }, 'api': { 'web': { 'get': [ 'exchange/public/product', 'assetWithdraw/getAllAsset.html', ], }, // the API structure below will need 3-layer apidefs 'sapi': { 'get': [ 'accountSnapshot', // these endpoints require this.apiKey 'margin/asset', 'margin/pair', 'margin/allAssets', 'margin/allPairs', 'margin/priceIndex', // these endpoints require this.apiKey + this.secret 'asset/assetDividend', 'margin/loan', 'margin/repay', 'margin/account', 'margin/transfer', 'margin/interestHistory', 'margin/forceLiquidationRec', 'margin/order', 'margin/openOrders', 'margin/allOrders', 'margin/myTrades', 'margin/maxBorrowable', 'margin/maxTransferable', 'futures/transfer', // https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi 'capital/config/getall', // get networks for withdrawing USDT ERC20 vs USDT Omni 'capital/deposit/address', 'capital/deposit/hisrec', 'capital/deposit/subAddress', 'capital/deposit/subHisrec', 'capital/withdraw/history', 'sub-account/futures/account', 'sub-account/futures/accountSummary', 'sub-account/futures/positionRisk', 'sub-account/margin/account', 'sub-account/margin/accountSummary', 'sub-account/status', // lending endpoints 'lending/daily/product/list', 'lending/daily/userLeftQuota', 'lending/daily/userRedemptionQuota', 'lending/daily/token/position', 'lending/union/account', 'lending/union/purchaseRecord', 'lending/union/redemptionRecord', 'lending/union/interestHistory', 'lending/project/list', 'lending/project/position/list', ], 'post': [ 'asset/dust', 'account/disableFastWithdrawSwitch', 'account/enableFastWithdrawSwitch', 'capital/withdraw/apply', 'margin/transfer', 'margin/loan', 'margin/repay', 'margin/order', 'sub-account/margin/enable', 'sub-account/margin/enable', 'sub-account/futures/enable', 'userDataStream', 'futures/transfer', // lending 'lending/customizedFixed/purchase', 'lending/daily/purchase', 'lending/daily/redeem', ], 'put': [ 'userDataStream', ], 'delete': [ 'margin/order', 'userDataStream', ], }, 'wapi': { 'post': [ 'withdraw', 'sub-account/transfer', ], 'get': [ 'depositHistory', 'withdrawHistory', 'depositAddress', 'accountStatus', 'systemStatus', 'apiTradingStatus', 'userAssetDribbletLog', 'tradeFee', 'assetDetail', 'sub-account/list', 'sub-account/transfer/history', 'sub-account/assets', ], }, 'fapiPublic': { 'get': [ 'ping', 'time', 'exchangeInfo', 'depth', 'trades', 'historicalTrades', 'aggTrades', 'klines', 'fundingRate', 'premiumIndex', 'ticker/24hr', 'ticker/price', 'ticker/bookTicker', 'allForceOrders', 'openInterest', 'leverageBracket', ], }, 'fapiPrivate': { 'get': [ 'allOrders', 'openOrder', 'openOrders', 'order', 'account', 'balance', 'positionMargin/history', 'positionRisk', 'userTrades', 'income', ], 'post': [ 'batchOrders', 'positionSide/dual', 'positionMargin', 'marginType', 'order', 'leverage', 'listenKey', ], 'put': [ 'listenKey', ], 'delete': [ 'batchOrders', 'order', 'allOpenOrders', 'listenKey', ], }, 'v3': { 'get': [ 'ticker/price', 'ticker/bookTicker', ], }, 'public': { 'get': [ 'ping', 'time', 'depth', 'trades', 'aggTrades', 'historicalTrades', 'klines', 'ticker/24hr', 'ticker/price', 'ticker/bookTicker', 'exchangeInfo', ], 'put': [ 'userDataStream' ], 'post': [ 'userDataStream' ], 'delete': [ 'userDataStream' ], }, 'private': { 'get': [ 'allOrderList', // oco 'openOrderList', // oco 'orderList', // oco 'order', 'openOrders', 'allOrders', 'account', 'myTrades', ], 'post': [ 'order/oco', 'order', 'order/test', ], 'delete': [ 'openOrders', // added on 2020-04-25 for canceling all open orders per symbol 'orderList', // oco 'order', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.001, 'maker': 0.001, }, }, 'commonCurrencies': { 'BCC': 'BCC', // kept for backward-compatibility https://github.com/ccxt/ccxt/issues/4848 'YOYO': 'YOYOW', }, // exchange-specific options 'options': { 'fetchTradesMethod': 'publicGetAggTrades', // publicGetTrades, publicGetHistoricalTrades 'fetchTickersMethod': 'publicGetTicker24hr', 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel 'defaultType': 'spot', // 'spot', 'future' 'hasAlreadyAuthenticatedSuccessfully': false, 'warnOnFetchOpenOrdersWithoutSymbol': true, 'recvWindow': 5 * 1000, // 5 sec, binance default 'timeDifference': 0, // the difference between system clock and Binance clock 'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation 'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision 'newOrderRespType': { 'market': 'FULL', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills 'limit': 'RESULT', // we change it from 'ACK' by default to 'RESULT' }, }, 'exceptions': { 'API key does not exist': AuthenticationError, 'Order would trigger immediately.': InvalidOrder, 'Account has insufficient balance for requested action.': InsufficientFunds, 'Rest API trading is not enabled.': ExchangeNotAvailable, "You don't have permission.": PermissionDenied, // {"msg":"You don't have permission.","success":false} 'Market is closed.': ExchangeNotAvailable, // {"code":-1013,"msg":"Market is closed."} '-1000': ExchangeNotAvailable, // {"code":-1000,"msg":"An unknown error occured while processing the request."} '-1003': RateLimitExceeded, // {"code":-1003,"msg":"Too much request weight used, current limit is 1200 request weight per 1 MINUTE. Please use the websocket for live updates to avoid polling the API."} '-1013': InvalidOrder, // createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL '-1021': InvalidNonce, // 'your time is ahead of server' '-1022': AuthenticationError, // {"code":-1022,"msg":"Signature for this request is not valid."} '-1100': InvalidOrder, // createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price' '-1104': ExchangeError, // Not all sent parameters were read, read 8 parameters but was sent 9 '-1128': ExchangeError, // {"code":-1128,"msg":"Combination of optional parameters invalid."} '-2010': ExchangeError, // generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc... '-2011': OrderNotFound, // cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER' '-2013': OrderNotFound, // fetchOrder (1, 'BTC/USDT') -> 'Order does not exist' '-2014': AuthenticationError, // { "code":-2014, "msg": "API-key format invalid." } '-2015': AuthenticationError, // "Invalid API-key, IP, or permissions for action." }, }); } setSandboxMode (enabled) { if (enabled) { // eslint-disable-line no-extra-boolean-cast if ('test' in this.urls) { const type = this.safeString (this.options, 'defaultType', 'spot'); if (type !== 'future') { throw new NotSupported (this.id + ' does not have a sandbox URL for ' + type + " markets, set exchange.options['defaultType'] = 'future' or don't use the sandbox for " + this.id); } if (typeof this.urls['api'] === 'string') { this.urls['api_backup'] = this.urls['api']; this.urls['api'] = this.urls['test']; } else { this.urls['api_backup'] = this.extend ({}, this.urls['api']); this.urls['api'] = this.extend ({}, this.urls['test']); } } else { throw new NotSupported (this.id + ' does not have a sandbox URL'); } } else if ('api_backup' in this.urls) { if (typeof this.urls['api'] === 'string') { this.urls['api'] = this.urls['api_backup']; } else { this.urls['api'] = this.extend ({}, this.urls['api_backup']); } } } nonce () { return this.milliseconds () - this.options['timeDifference']; } async fetchTime (params = {}) { const type = this.safeString2 (this.options, 'fetchTime', 'defaultType', 'spot'); const method = (type === 'spot') ? 'publicGetTime' : 'fapiPublicGetTime'; const response = await this[method] (params); return this.safeInteger (response, 'serverTime'); } async loadTimeDifference () { const serverTime = await this.fetchTime (); const after = this.milliseconds (); this.options['timeDifference'] = after - serverTime; return this.options['timeDifference']; } async fetchMarkets (params = {}) { const defaultType = this.safeString2 (this.options, 'fetchMarkets', 'defaultType', 'spot'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); const method = (type === 'spot') ? 'publicGetExchangeInfo' : 'fapiPublicGetExchangeInfo'; const response = await this[method] (query); // // spot // // { // "timezone":"UTC", // "serverTime":1575416692969, // "rateLimits":[ // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, // {"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":100}, // {"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":200000} // ], // "exchangeFilters":[], // "symbols":[ // { // "symbol":"ETHBTC", // "status":"TRADING", // "baseAsset":"ETH", // "baseAssetPrecision":8, // "quoteAsset":"BTC", // "quotePrecision":8, // "baseCommissionPrecision":8, // "quoteCommissionPrecision":8, // "orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"], // "icebergAllowed":true, // "ocoAllowed":true, // "quoteOrderQtyMarketAllowed":true, // "isSpotTradingAllowed":true, // "isMarginTradingAllowed":true, // "filters":[ // {"filterType":"PRICE_FILTER","minPrice":"0.00000100","maxPrice":"100000.00000000","tickSize":"0.00000100"}, // {"filterType":"PERCENT_PRICE","multiplierUp":"5","multiplierDown":"0.2","avgPriceMins":5}, // {"filterType":"LOT_SIZE","minQty":"0.00100000","maxQty":"100000.00000000","stepSize":"0.00100000"}, // {"filterType":"MIN_NOTIONAL","minNotional":"0.00010000","applyToMarket":true,"avgPriceMins":5}, // {"filterType":"ICEBERG_PARTS","limit":10}, // {"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"63100.00000000","stepSize":"0.00000000"}, // {"filterType":"MAX_NUM_ALGO_ORDERS","maxNumAlgoOrders":5} // ] // }, // ], // } // // futures (fapi) // // { // "timezone":"UTC", // "serverTime":1575417244353, // "rateLimits":[ // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, // {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200} // ], // "exchangeFilters":[], // "symbols":[ // { // "symbol":"BTCUSDT", // "status":"TRADING", // "maintMarginPercent":"2.5000", // "requiredMarginPercent":"5.0000", // "baseAsset":"BTC", // "quoteAsset":"USDT", // "pricePrecision":2, // "quantityPrecision":3, // "baseAssetPrecision":8, // "quotePrecision":8, // "filters":[ // {"minPrice":"0.01","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.01"}, // {"stepSize":"0.001","filterType":"LOT_SIZE","maxQty":"1000","minQty":"0.001"}, // {"stepSize":"0.001","filterType":"MARKET_LOT_SIZE","maxQty":"1000","minQty":"0.001"}, // {"limit":200,"filterType":"MAX_NUM_ORDERS"}, // {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"} // ], // "orderTypes":["LIMIT","MARKET","STOP"], // "timeInForce":["GTC","IOC","FOK","GTX"] // } // ] // } // if (this.options['adjustForTimeDifference']) { await this.loadTimeDifference (); } const markets = this.safeValue (response, 'symbols'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const future = ('maintMarginPercent' in market); const spot = !future; const marketType = spot ? 'spot' : 'future'; const id = this.safeString (market, 'symbol'); const lowercaseId = this.safeStringLower (market, 'symbol'); const baseId = market['baseAsset']; const quoteId = market['quoteAsset']; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const filters = this.indexBy (market['filters'], 'filterType'); const precision = { 'base': market['baseAssetPrecision'], 'quote': market['quotePrecision'], 'amount': market['baseAssetPrecision'], 'price': market['quotePrecision'], }; const status = this.safeString (market, 'status'); const active = (status === 'TRADING'); const entry = { 'id': id, 'lowercaseId': lowercaseId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'type': marketType, 'spot': spot, 'future': future, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }; if ('PRICE_FILTER' in filters) { const filter = filters['PRICE_FILTER']; // PRICE_FILTER reports zero values for maxPrice // since they updated filter types in November 2018 // https://github.com/ccxt/ccxt/issues/4286 // therefore limits['price']['max'] doesn't have any meaningful value except undefined entry['limits']['price'] = { 'min': this.safeFloat (filter, 'minPrice'), 'max': undefined, }; const maxPrice = this.safeFloat (filter, 'maxPrice'); if ((maxPrice !== undefined) && (maxPrice > 0)) { entry['limits']['price']['max'] = maxPrice; } entry['precision']['price'] = this.precisionFromString (filter['tickSize']); } if ('LOT_SIZE' in filters) { const filter = this.safeValue (filters, 'LOT_SIZE', {}); const stepSize = this.safeString (filter, 'stepSize'); entry['precision']['amount'] = this.precisionFromString (stepSize); entry['limits']['amount'] = { 'min': this.safeFloat (filter, 'minQty'), 'max': this.safeFloat (filter, 'maxQty'), }; } if ('MARKET_LOT_SIZE' in filters) { const filter = this.safeValue (filters, 'MARKET_LOT_SIZE', {}); entry['limits']['market'] = { 'min': this.safeFloat (filter, 'minQty'), 'max': this.safeFloat (filter, 'maxQty'), }; } if ('MIN_NOTIONAL' in filters) { entry['limits']['cost']['min'] = this.safeFloat (filters['MIN_NOTIONAL'], 'minNotional'); } result.push (entry); } return result; } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; let key = 'quote'; const rate = market[takerOrMaker]; let cost = amount * rate; let precision = market['precision']['price']; if (side === 'sell') { cost *= price; } else { key = 'base'; precision = market['precision']['amount']; } cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': parseFloat (cost), }; } async fetchBalance (params = {}) { await this.loadMarkets (); const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType', 'spot'); const type = this.safeString (params, 'type', defaultType); const method = (type === 'spot') ? 'privateGetAccount' : 'fapiPrivateGetAccount'; const query = this.omit (params, 'type'); const response = await this[method] (query); // // spot // // { // makerCommission: 10, // takerCommission: 10, // buyerCommission: 0, // sellerCommission: 0, // canTrade: true, // canWithdraw: true, // canDeposit: true, // updateTime: 1575357359602, // accountType: "MARGIN", // balances: [ // { asset: "BTC", free: "0.00219821", locked: "0.00000000" }, // ] // } // // futures (fapi) // // { // "feeTier":0, // "canTrade":true, // "canDeposit":true, // "canWithdraw":true, // "updateTime":0, // "totalInitialMargin":"0.00000000", // "totalMaintMargin":"0.00000000", // "totalWalletBalance":"4.54000000", // "totalUnrealizedProfit":"0.00000000", // "totalMarginBalance":"4.54000000", // "totalPositionInitialMargin":"0.00000000", // "totalOpenOrderInitialMargin":"0.00000000", // "maxWithdrawAmount":"4.54000000", // "assets":[ // { // "asset":"USDT", // "walletBalance":"4.54000000", // "unrealizedProfit":"0.00000000", // "marginBalance":"4.54000000", // "maintMargin":"0.00000000", // "initialMargin":"0.00000000", // "positionInitialMargin":"0.00000000", // "openOrderInitialMargin":"0.00000000", // "maxWithdrawAmount":"4.54000000" // } // ], // "positions":[ // { // "symbol":"BTCUSDT", // "initialMargin":"0.00000", // "maintMargin":"0.00000", // "unrealizedProfit":"0.00000000", // "positionInitialMargin":"0.00000", // "openOrderInitialMargin":"0.00000" // } // ] // } // const result = { 'info': response }; if (type === 'spot') { const balances = this.safeValue (response, 'balances', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'asset'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'free'); account['used'] = this.safeFloat (balance, 'locked'); result[code] = account; } } else { const balances = this.safeValue (response, 'assets', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'asset'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['used'] = this.safeFloat (balance, 'initialMargin'); account['total'] = this.safeFloat (balance, 'marginBalance'); result[code] = account; } } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 100, max 5000, see https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#order-book } const method = market['spot'] ? 'publicGetDepth' : 'fapiPublicGetDepth'; const response = await this[method] (this.extend (request, params)); const orderbook = this.parseOrderBook (response); orderbook['nonce'] = this.safeInteger (response, 'lastUpdateId'); return orderbook; } parseTicker (ticker, market = undefined) { const timestamp = this.safeInteger (ticker, 'closeTime'); let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'lastPrice'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'highPrice'), 'low': this.safeFloat (ticker, 'lowPrice'), 'bid': this.safeFloat (ticker, 'bidPrice'), 'bidVolume': this.safeFloat (ticker, 'bidQty'), 'ask': this.safeFloat (ticker, 'askPrice'), 'askVolume': this.safeFloat (ticker, 'askQty'), 'vwap': this.safeFloat (ticker, 'weightedAvgPrice'), 'open': this.safeFloat (ticker, 'openPrice'), 'close': last, 'last': last, 'previousClose': this.safeFloat (ticker, 'prevClosePrice'), // previous day close 'change': this.safeFloat (ticker, 'priceChange'), 'percentage': this.safeFloat (ticker, 'priceChangePercent'), 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), 'info': ticker, }; } async fetchStatus (params = {}) { const response = await this.wapiGetSystemStatus (params); let status = this.safeValue (response, 'status'); if (status !== undefined) { status = (status === 0) ? 'ok' : 'maintenance'; this.status = this.extend (this.status, { 'status': status, 'updated': this.milliseconds (), }); } return this.status; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const method = market['spot'] ? 'publicGetTicker24hr' : 'fapiPublicGetTicker24hr'; const response = await this[method] (this.extend (request, params)); return this.parseTicker (response, market); } parseTickers (rawTickers, symbols = undefined) { const tickers = []; for (let i = 0; i < rawTickers.length; i++) { tickers.push (this.parseTicker (rawTickers[i])); } return this.filterByArray (tickers, 'symbol', symbols); } async fetchBidsAsks (symbols = undefined, params = {}) { await this.loadMarkets (); const defaultType = this.safeString2 (this.options, 'fetchBidsAsks', 'defaultType', 'spot'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); const method = (type === 'spot') ? 'publicGetTickerBookTicker' : 'fapiPublicGetTickerBookTicker'; const response = await this[method] (query); return this.parseTickers (response, symbols); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const method = this.options['fetchTickersMethod']; const response = await this[method] (params); return this.parseTickers (response, symbols); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ this.safeInteger (ohlcv, 0), this.safeFloat (ohlcv, 1), this.safeFloat (ohlcv, 2), this.safeFloat (ohlcv, 3), this.safeFloat (ohlcv, 4), this.safeFloat (ohlcv, 5), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'interval': this.timeframes[timeframe], }; if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['limit'] = limit; // default == max == 500 } const method = market['spot'] ? 'publicGetKlines' : 'fapiPublicGetKlines'; const response = await this[method] (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { if ('isDustTrade' in trade) { return this.parseDustTrade (trade, market); } // // aggregate trades // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list // // { // "a": 26129, // Aggregate tradeId // "p": "0.01633102", // Price // "q": "4.70443515", // Quantity // "f": 27781, // First tradeId // "l": 27781, // Last tradeId // "T": 1498793709153, // Timestamp // "m": true, // Was the buyer the maker? // "M": true // Was the trade the best price match? // } // // recent public trades and old public trades // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#recent-trades-list // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#old-trade-lookup-market_data // // { // "id": 28457, // "price": "4.00000100", // "qty": "12.00000000", // "time": 1499865549590, // "isBuyerMaker": true, // "isBestMatch": true // } // // private trades // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#account-trade-list-user_data // // { // "symbol": "BNBBTC", // "id": 28457, // "orderId": 100234, // "price": "4.00000100", // "qty": "12.00000000", // "commission": "10.10000000", // "commissionAsset": "BNB", // "time": 1499865549590, // "isBuyer": true, // "isMaker": false, // "isBestMatch": true // } // // futures trades // https://binance-docs.github.io/apidocs/futures/en/#account-trade-list-user_data // // { // "accountId": 20, // "buyer": False, // "commission": "-0.07819010", // "commissionAsset": "USDT", // "counterPartyId": 653, // "id": 698759, // "maker": False, // "orderId": 25851813, // "price": "7819.01", // "qty": "0.002", // "quoteQty": "0.01563", // "realizedPnl": "-0.91539999", // "side": "SELL", // "symbol": "BTCUSDT", // "time": 1569514978020 // } // const timestamp = this.safeInteger2 (trade, 'T', 'time'); const price = this.safeFloat2 (trade, 'p', 'price'); const amount = this.safeFloat2 (trade, 'q', 'qty'); const id = this.safeString2 (trade, 'a', 'id'); let side = undefined; const orderId = this.safeString (trade, 'orderId'); if ('m' in trade) { side = trade['m'] ? 'sell' : 'buy'; // this is reversed intentionally } else if ('isBuyerMaker' in trade) { side = trade['isBuyerMaker'] ? 'sell' : 'buy'; } else if ('side' in trade) { side = this.safeStringLower (trade, 'side'); } else { if ('isBuyer' in trade) { side = trade['isBuyer'] ? 'buy' : 'sell'; // this is a true side } } let fee = undefined; if ('commission' in trade) { fee = { 'cost': this.safeFloat (trade, 'commission'), 'currency': this.safeCurrencyCode (this.safeString (trade, 'commissionAsset')), }; } let takerOrMaker = undefined; if ('isMaker' in trade) { takerOrMaker = trade['isMaker'] ? 'maker' : 'taker'; } let symbol = undefined; if (market === undefined) { const marketId = this.safeString (trade, 'symbol'); market = this.safeValue (this.markets_by_id, marketId); } if (market !== undefined) { symbol = market['symbol']; } let cost = undefined; if ((price !== undefined) && (amount !== undefined)) { cost = price * amount; } return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], // 'fromId': 123, // ID to get aggregate trades from INCLUSIVE. // 'startTime': 456, // Timestamp in ms to get aggregate trades from INCLUSIVE. // 'endTime': 789, // Timestamp in ms to get aggregate trades until INCLUSIVE. // 'limit': 500, // default = 500, maximum = 1000 }; const defaultType = this.safeString2 (this.options, 'fetchTrades', 'defaultType', 'spot'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); const defaultMethod = (type === 'future') ? 'fapiPublicGetTrades' : 'publicGetTrades'; let method = this.safeString (this.options, 'fetchTradesMethod', defaultMethod); if (method === 'publicGetAggTrades') { if (since !== undefined) { request['startTime'] = since; // https://github.com/ccxt/ccxt/issues/6400 // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list request['endTime'] = this.sum (since, 3600000); } if (type === 'future') { method = 'fapiPublicGetAggTrades'; } } else if ((method === 'publicGetHistoricalTrades') && (type === 'future')) { method = 'fapiPublicGetHistoricalTrades'; } if (limit !== undefined) { request['limit'] = limit; // default = 500, maximum = 1000 } // // Caveats: // - default limit (500) applies only if no other parameters set, trades up // to the maximum limit may be returned to satisfy other parameters // - if both limit and time window is set and time window contains more // trades than the limit then the last trades from the window are returned // - 'tradeId' accepted and returned by this method is "aggregate" trade id // which is different from actual trade id // - setting both fromId and time window results in error const response = await this[method] (this.extend (request, query)); // // aggregate trades // // [ // { // "a": 26129, // Aggregate tradeId // "p": "0.01633102", // Price // "q": "4.70443515", // Quantity // "f": 27781, // First tradeId // "l": 27781, // Last tradeId // "T": 1498793709153, // Timestamp // "m": true, // Was the buyer the maker? // "M": true // Was the trade the best price match? // } // ] // // recent public trades and historical public trades // // [ // { // "id": 28457, // "price": "4.00000100", // "qty": "12.00000000", // "time": 1499865549590, // "isBuyerMaker": true, // "isBestMatch": true // } // ] // return this.parseTrades (response, market, since, limit); } parseOrderStatus (status) { const statuses = { 'NEW': 'open', 'PARTIALLY_FILLED': 'open', 'FILLED': 'closed', 'CANCELED': 'canceled', 'PENDING_CANCEL': 'canceling', // currently unused 'REJECTED': 'rejected', 'EXPIRED': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // spot // // { // "symbol": "LTCBTC", // "orderId": 1, // "clientOrderId": "myOrder1", // "price": "0.1", // "origQty": "1.0", // "executedQty": "0.0", // "cummulativeQuoteQty": "0.0", // "status": "NEW", // "timeInForce": "GTC", // "type": "LIMIT", // "side": "BUY", // "stopPrice": "0.0", // "icebergQty": "0.0", // "time": 1499827319559, // "updateTime": 1499827319559, // "isWorking": true // } // // futures // // { // "symbol": "BTCUSDT", // "orderId": 1, // "clientOrderId": "myOrder1", // "price": "0.1", // "origQty": "1.0", // "executedQty": "1.0", // "cumQuote": "10.0", // "status": "NEW", // "timeInForce": "GTC", // "type": "LIMIT", // "side": "BUY", // "stopPrice": "0.0", // "updateTime": 1499827319559 // } // const status = this.parseOrderStatus (this.safeString (order, 'status')); let symbol = undefined; const marketId = this.safeString (order, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if (market !== undefined) { symbol = market['symbol']; } let timestamp = undefined; if ('time' in order) { timestamp = this.safeInteger (order, 'time'); } else if ('transactTime' in order) { timestamp = this.safeInteger (order, 'transactTime'); } let price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'origQty'); const filled = this.safeFloat (order, 'executedQty'); let remaining = undefined; // - Spot/Margin market: cummulativeQuoteQty // - Futures market: cumQuote. // Note this is not the actual cost, since Binance futures uses leverage to calculate margins. let cost = this.safeFloat2 (order, 'cummulativeQuoteQty', 'cumQuote'); if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; if (this.options['parseOrderToPrecision']) { remaining = parseFloat (this.amountToPrecision (symbol, remaining)); } remaining = Math.max (remaining, 0.0); } if (price !== undefined) { if (cost === undefined) { cost = price * filled; } } } const id = this.safeString (order, 'orderId'); let type = this.safeStringLower (order, 'type'); if (type === 'market') { if (price === 0.0) { if ((cost !== undefined) && (filled !== undefined)) { if ((cost > 0) && (filled > 0)) { price = cost / filled; if (this.options['parseOrderToPrecision']) { price = parseFloat (this.priceToPrecision (symbol, price)); } } } } } else if (type === 'limit_maker') { type = 'limit'; } const side = this.safeStringLower (order, 'side'); let fee = undefined; let trades = undefined; const fills = this.safeValue (order, 'fills'); if (fills !== undefined) { trades = this.parseTrades (fills, market); const numTrades = trades.length; if (numTrades > 0) { cost = trades[0]['cost']; fee = { 'cost': trades[0]['fee']['cost'], 'currency': trades[0]['fee']['currency'], }; for (let i = 1; i < trades.length; i++) { cost = this.sum (cost, trades[i]['cost']); fee['cost'] = this.sum (fee['cost'], trades[i]['fee']['cost']); } } } let average = undefined; if (cost !== undefined) { if (filled) { average = cost / filled; if (this.options['parseOrderToPrecision']) { average = parseFloat (this.priceToPrecision (symbol, average)); } } if (this.options['parseOrderToPrecision']) { cost = parseFloat (this.costToPrecision (symbol, cost)); } } const clientOrderId = this.safeString (order, 'clientOrderId'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': trades, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); // the next 5 lines are added to support for testing orders let method = market['spot'] ? 'privatePostOrder' : 'fapiPrivatePostOrder'; if (market['spot']) { const test = this.safeValue (params, 'test', false); if (test) { method += 'Test'; params = this.omit (params, 'test'); } } const uppercaseType = type.toUpperCase (); const validOrderTypes = this.safeValue (market['info'], 'orderTypes'); if (!this.inArray (uppercaseType, validOrderTypes)) { throw new InvalidOrder (this.id + ' ' + type + ' is not a valid order type in ' + market['type'] + ' market ' + symbol); } const request = { 'symbol': market['id'], 'type': uppercaseType, 'side': side.toUpperCase (), }; if (uppercaseType === 'MARKET') { const quoteOrderQty = this.safeFloat (params, 'quoteOrderQty'); const precision = market['precision']['price']; if (quoteOrderQty !== undefined) { request['quoteOrderQty'] = this.decimalToPrecision (quoteOrderQty, TRUNCATE, precision, this.precisionMode); params = this.omit (params, 'quoteOrderQty'); } else if (price !== undefined) { request['quoteOrderQty'] = this.decimalToPrecision (amount * price, TRUNCATE, precision, this.precisionMode); } else { request['quantity'] = this.amountToPrecision (symbol, amount); } } else { request['quantity'] = this.amountToPrecision (symbol, amount); } if (market['spot']) { request['newOrderRespType'] = this.safeValue (this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills } let timeInForceIsRequired = false; let priceIsRequired = false; let stopPriceIsRequired = false; if (uppercaseType === 'LIMIT') { priceIsRequired = true; timeInForceIsRequired = true; } else if ((uppercaseType === 'STOP_LOSS') || (uppercaseType === 'TAKE_PROFIT')) { stopPriceIsRequired = true; if (market['future']) { priceIsRequired = true; } } else if ((uppercaseType === 'STOP_LOSS_LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) { stopPriceIsRequired = true; priceIsRequired = true; timeInForceIsRequired = true; } else if (uppercaseType === 'LIMIT_MAKER') { priceIsRequired = true; } else if (uppercaseType === 'STOP') { stopPriceIsRequired = true; priceIsRequired = true; } else if (uppercaseType === 'STOP_MARKET') { stopPriceIsRequired = true; } if (priceIsRequired) { if (price === undefined) { throw new InvalidOrder (this.id + ' createOrder method requires a price argument for a ' + type + ' order'); } request['price'] = this.priceToPrecision (symbol, price); } if (timeInForceIsRequired) { request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel } if (stopPriceIsRequired) { const stopPrice = this.safeFloat (params, 'stopPrice'); if (stopPrice === undefined) { throw new InvalidOrder (this.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order'); } else { params = this.omit (params, 'stopPrice'); request['stopPrice'] = this.priceToPrecision (symbol, stopPrice); } } const response = await this[method] (this.extend (request, params)); return this.parseOrder (response, market); } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const method = market['spot'] ? 'privateGetOrder' : 'fapiPrivateGetOrder'; const request = { 'symbol': market['id'], }; const origClientOrderId = this.safeValue (params, 'origClientOrderId'); if (origClientOrderId !== undefined) { request['origClientOrderId'] = origClientOrderId; } else { request['orderId'] = parseInt (id); } const response = await this[method] (this.extend (request, params)); return this.parseOrder (response, market); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['limit'] = limit; } const method = market['spot'] ? 'privateGetAllOrders' : 'fapiPrivateGetAllOrders'; const response = await this[method] (this.extend (request, params)); // // Spot: // [ // { // "symbol": "LTCBTC", // "orderId": 1, // "clientOrderId": "myOrder1", // "price": "0.1", // "origQty": "1.0", // "executedQty": "0.0", // "cummulativeQuoteQty": "0.0", // "status": "NEW", // "timeInForce": "GTC", // "type": "LIMIT", // "side": "BUY", // "stopPrice": "0.0", // "icebergQty": "0.0", // "time": 1499827319559, // "updateTime": 1499827319559, // "isWorking": true // } // ] // // Futures: // [ // { // "symbol": "BTCUSDT", // "orderId": 1, // "clientOrderId": "myOrder1", // "price": "0.1", // "origQty": "1.0", // "executedQty": "1.0", // "cumQuote": "10.0", // "status": "NEW", // "timeInForce": "GTC", // "type": "LIMIT", // "side": "BUY", // "stopPrice": "0.0", // "updateTime": 1499827319559 // } // ] // return this.parseOrders (response, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; let query = undefined; let type = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; type = market['type']; query = params; } else if (this.options['warnOnFetchOpenOrdersWithoutSymbol']) { const symbols = this.symbols; const numSymbols = symbols.length; const fetchOpenOrdersRateLimit = parseInt (numSymbols / 2); throw new ExchangeError (this.id + ' fetchOpenOrders WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + fetchOpenOrdersRateLimit.toString () + ' seconds. Do not call this method frequently to avoid ban. Set ' + this.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = false to suppress this warning message.'); } else { const defaultType = this.safeString2 (this.options, 'fetchOpenOrders', 'defaultType', 'spot'); type = this.safeString (params, 'type', defaultType); query = this.omit (params, 'type'); } const method = (type === 'spot') ? 'privateGetOpenOrders' : 'fapiPrivateGetOpenOrders'; const response = await this[method] (this.extend (request, query)); return this.parseOrders (response, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'closed'); } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); // https://github.com/ccxt/ccxt/issues/6507 const origClientOrderId = this.safeValue (params, 'origClientOrderId'); const request = { 'symbol': market['id'], // 'orderId': parseInt (id), // 'origClientOrderId': id, }; if (origClientOrderId === undefined) { request['orderId'] = parseInt (id); } else { request['origClientOrderId'] = origClientOrderId; } const method = market['spot'] ? 'privateDeleteOrder' : 'fapiPrivateDeleteOrder'; const response = await this[method] (this.extend (request, params)); return this.parseOrder (response); } async cancelAllOrders (symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const defaultType = this.safeString2 (this.options, 'cancelAllOrders', 'defaultType', 'spot'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); const method = (type === 'spot') ? 'privateDeleteOpenOrders' : 'fapiPrivateDeleteAllOpenOrders'; const response = await this[method] (this.extend (request, query)); if (Array.isArray (response)) { return this.parseOrders (response, market); } else { return response; } } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const method = market['spot'] ? 'privateGetMyTrades' : 'fapiPrivateGetUserTrades'; const request = { 'symbol': market['id'], }; if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['limit'] = limit; } const response = await this[method] (this.extend (request, params)); // // spot trade // [ // { // "symbol": "BNBBTC", // "id": 28457, // "orderId": 100234, // "price": "4.00000100", // "qty": "12.00000000", // "commission": "10.10000000", // "commissionAsset": "BNB", // "time": 1499865549590, // "isBuyer": true, // "isMaker": false, // "isBestMatch": true, // } // ] // // futures trade // // [ // { // "accountId": 20, // "buyer": False, // "commission": "-0.07819010", // "commissionAsset": "USDT", // "counterPartyId": 653, // "id": 698759, // "maker": False, // "orderId": 25851813, // "price": "7819.01", // "qty": "0.002", // "quoteQty": "0.01563", // "realizedPnl": "-0.91539999", // "side": "SELL", // "symbol": "BTCUSDT", // "time": 1569514978020 // } // ] return this.parseTrades (response, market, since, limit); } async fetchMyDustTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { // // Binance provides an opportunity to trade insignificant (i.e. non-tradable and non-withdrawable) // token leftovers (of any asset) into `BNB` coin which in turn can be used to pay trading fees with it. // The corresponding trades history is called the `Dust Log` and can be requested via the following end-point: // https://github.com/binance-exchange/binance-official-api-docs/blob/master/wapi-api.md#dustlog-user_data // await this.loadMarkets (); const response = await this.wapiGetUserAssetDribbletLog (params); // { success: true, // results: { total: 1, // rows: [ { transfered_total: "1.06468458", // service_charge_total: "0.02172826", // tran_id: 2701371634, // logs: [ { tranId: 2701371634, // serviceChargeAmount: "0.00012819", // uid: "35103861", // amount: "0.8012", // operateTime: "2018-10-07 17:56:07", // transferedAmount: "0.00628141", // fromAsset: "ADA" } ], // operate_time: "2018-10-07 17:56:06" } ] } } const results = this.safeValue (response, 'results', {}); const rows = this.safeValue (results, 'rows', []); const data = []; for (let i = 0; i < rows.length; i++) { const logs = rows[i]['logs']; for (let j = 0; j < logs.length; j++) { logs[j]['isDustTrade'] = true; data.push (logs[j]); } } const trades = this.parseTrades (data, undefined, since, limit); return this.filterBySinceLimit (trades, since, limit); } parseDustTrade (trade, market = undefined) { // { tranId: 2701371634, // serviceChargeAmount: "0.00012819", // uid: "35103861", // amount: "0.8012", // operateTime: "2018-10-07 17:56:07", // transferedAmount: "0.00628141", // fromAsset: "ADA" }, const orderId = this.safeString (trade, 'tranId'); const timestamp = this.parse8601 (this.safeString (trade, 'operateTime')); const tradedCurrency = this.safeCurrencyCode (this.safeString (trade, 'fromAsset')); const earnedCurrency = this.currency ('BNB')['code']; const applicantSymbol = earnedCurrency + '/' + tradedCurrency; let tradedCurrencyIsQuote = false; if (applicantSymbol in this.markets) { tradedCurrencyIsQuote = true; } // // Warning // Binance dust trade `fee` is already excluded from the `BNB` earning reported in the `Dust Log`. // So the parser should either set the `fee.cost` to `0` or add it on top of the earned // BNB `amount` (or `cost` depending on the trade `side`). The second of the above options // is much more illustrative and therefore preferable. // const fee = { 'currency': earnedCurrency, 'cost': this.safeFloat (trade, 'serviceChargeAmount'), }; let symbol = undefined; let amount = undefined; let cost = undefined; let side = undefined; if (tradedCurrencyIsQuote) { symbol = applicantSymbol; amount = this.sum (this.safeFloat (trade, 'transferedAmount'), fee['cost']); cost = this.safeFloat (trade, 'amount'); side = 'buy'; } else { symbol = tradedCurrency + '/' + earnedCurrency; amount = this.safeFloat (trade, 'amount'); cost = this.sum (this.safeFloat (trade, 'transferedAmount'), fee['cost']); side = 'sell'; } let price = undefined; if (cost !== undefined) { if (amount) { price = cost / amount; } } const id = undefined; const type = undefined; const takerOrMaker = undefined; return { 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': type, 'takerOrMaker': takerOrMaker, 'side': side, 'amount': amount, 'price': price, 'cost': cost, 'fee': fee, 'info': trade, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = {}; if (code !== undefined) { currency = this.currency (code); request['asset'] = currency['id']; } if (since !== undefined) { request['startTime'] = since; // max 3 months range https://github.com/ccxt/ccxt/issues/6495 request['endTime'] = this.sum (since, 7776000000); } const response = await this.wapiGetDepositHistory (this.extend (request, params)); // // { success: true, // depositList: [ { insertTime: 1517425007000, // amount: 0.3, // address: "0x0123456789abcdef", // addressTag: "", // txId: "0x0123456789abcdef", // asset: "ETH", // status: 1 } ] } // return this.parseTransactions (response['depositList'], currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = {}; if (code !== undefined) { currency = this.currency (code); request['asset'] = currency['id']; } if (since !== undefined) { request['startTime'] = since; // max 3 months range https://github.com/ccxt/ccxt/issues/6495 request['endTime'] = this.sum (since, 7776000000); } const response = await this.wapiGetWithdrawHistory (this.extend (request, params)); // // { withdrawList: [ { amount: 14, // address: "0x0123456789abcdef...", // successTime: 1514489710000, // transactionFee: 0.01, // addressTag: "", // txId: "0x0123456789abcdef...", // id: "0123456789abcdef...", // asset: "ETH", // applyTime: 1514488724000, // status: 6 }, // { amount: 7600, // address: "0x0123456789abcdef...", // successTime: 1515323226000, // transactionFee: 0.01, // addressTag: "", // txId: "0x0123456789abcdef...", // id: "0123456789abcdef...", // asset: "ICN", // applyTime: 1515322539000, // status: 6 } ], // success: true } // return this.parseTransactions (response['withdrawList'], currency, since, limit); } parseTransactionStatusByType (status, type = undefined) { const statusesByType = { 'deposit': { '0': 'pending', '1': 'ok', }, 'withdrawal': { '0': 'pending', // Email Sent '1': 'canceled', // Cancelled (different from 1 = ok in deposits) '2': 'pending', // Awaiting Approval '3': 'failed', // Rejected '4': 'pending', // Processing '5': 'failed', // Failure '6': 'ok', // Completed }, }; const statuses = this.safeValue (statusesByType, type, {}); return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // insertTime: 1517425007000, // amount: 0.3, // address: "0x0123456789abcdef", // addressTag: "", // txId: "0x0123456789abcdef", // asset: "ETH", // status: 1 // } // // fetchWithdrawals // // { // amount: 14, // address: "0x0123456789abcdef...", // successTime: 1514489710000, // transactionFee: 0.01, // addressTag: "", // txId: "0x0123456789abcdef...", // id: "0123456789abcdef...", // asset: "ETH", // applyTime: 1514488724000, // status: 6 // } // const id = this.safeString (transaction, 'id'); const address = this.safeString (transaction, 'address'); let tag = this.safeString (transaction, 'addressTag'); // set but unused if (tag !== undefined) { if (tag.length < 1) { tag = undefined; } } const txid = this.safeString (transaction, 'txId'); const currencyId = this.safeString (transaction, 'asset'); const code = this.safeCurrencyCode (currencyId, currency); let timestamp = undefined; const insertTime = this.safeInteger (transaction, 'insertTime'); const applyTime = this.safeInteger (transaction, 'applyTime'); let type = this.safeString (transaction, 'type'); if (type === undefined) { if ((insertTime !== undefined) && (applyTime === undefined)) { type = 'deposit'; timestamp = insertTime; } else if ((insertTime === undefined) && (applyTime !== undefined)) { type = 'withdrawal'; timestamp = applyTime; } } const status = this.parseTransactionStatusByType (this.safeString (transaction, 'status'), type); const amount = this.safeFloat (transaction, 'amount'); const feeCost = this.safeFloat (transaction, 'transactionFee'); let fee = undefined; if (feeCost !== undefined) { fee = { 'currency': code, 'cost': feeCost }; } return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': fee, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset': currency['id'], }; const response = await this.wapiGetDepositAddress (this.extend (request, params)); const success = this.safeValue (response, 'success'); if ((success === undefined) || !success) { throw new InvalidAddress (this.id + ' fetchDepositAddress returned an empty response – create the deposit address in the user settings first.'); } const address = this.safeString (response, 'address'); const tag = this.safeString (response, 'addressTag'); this.checkAddress (address); return { 'currency': code, 'address': this.checkAddress (address), 'tag': tag, 'info': response, }; } async fetchFundingFees (codes = undefined, params = {}) { const response = await this.wapiGetAssetDetail (params); // // { // "success": true, // "assetDetail": { // "CTR": { // "minWithdrawAmount": "70.00000000", //min withdraw amount // "depositStatus": false,//deposit status // "withdrawFee": 35, // withdraw fee // "withdrawStatus": true, //withdraw status // "depositTip": "Delisted, Deposit Suspended" //reason // }, // "SKY": { // "minWithdrawAmount": "0.02000000", // "depositStatus": true, // "withdrawFee": 0.01, // "withdrawStatus": true // } // } // } // const detail = this.safeValue (response, 'assetDetail', {}); const ids = Object.keys (detail); const withdrawFees = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const code = this.safeCurrencyCode (id); withdrawFees[code] = this.safeFloat (detail[id], 'withdrawFee'); } return { 'withdraw': withdrawFees, 'deposit': {}, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); // name is optional, can be overrided via params const name = address.slice (0, 20); const request = { 'asset': currency['id'], 'address': address, 'amount': parseFloat (amount), 'name': name, // name is optional, can be overrided via params // https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi // issue sapiGetCapitalConfigGetall () to get networks for withdrawing USDT ERC20 vs USDT Omni // 'network': 'ETH', // 'BTC', 'TRX', etc, optional }; if (tag !== undefined) { request['addressTag'] = tag; } const response = await this.wapiPostWithdraw (this.extend (request, params)); return { 'info': response, 'id': this.safeString (response, 'id'), }; } parseTradingFee (fee, market = undefined) { // // { // "symbol": "ADABNB", // "maker": 0.9000, // "taker": 1.0000 // } // const marketId = this.safeString (fee, 'symbol'); let symbol = marketId; if (marketId in this.markets_by_id) { const market = this.markets_by_id[marketId]; symbol = market['symbol']; } return { 'info': fee, 'symbol': symbol, 'maker': this.safeFloat (fee, 'maker'), 'taker': this.safeFloat (fee, 'taker'), }; } async fetchTradingFee (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.wapiGetTradeFee (this.extend (request, params)); // // { // "tradeFee": [ // { // "symbol": "ADABNB", // "maker": 0.9000, // "taker": 1.0000 // } // ], // "success": true // } // const tradeFee = this.safeValue (response, 'tradeFee', []); const first = this.safeValue (tradeFee, 0, {}); return this.parseTradingFee (first); } async fetchTradingFees (params = {}) { await this.loadMarkets (); const response = await this.wapiGetTradeFee (params); // // { // "tradeFee": [ // { // "symbol": "ADABNB", // "maker": 0.9000, // "taker": 1.0000 // } // ], // "success": true // } // const tradeFee = this.safeValue (response, 'tradeFee', []); const result = {}; for (let i = 0; i < tradeFee.length; i++) { const fee = this.parseTradingFee (tradeFee[i]); const symbol = fee['symbol']; result[symbol] = fee; } return result; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; url += '/' + path; if (api === 'wapi') { url += '.html'; } const userDataStream = (path === 'userDataStream') || (path === 'listenKey'); if (path === 'historicalTrades') { if (this.apiKey) { headers = { 'X-MBX-APIKEY': this.apiKey, }; } else { throw new AuthenticationError (this.id + ' historicalTrades endpoint requires `apiKey` credential'); } } else if (userDataStream) { if (this.apiKey) { // v1 special case for userDataStream body = this.urlencode (params); headers = { 'X-MBX-APIKEY': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded', }; } else { throw new AuthenticationError (this.id + ' userDataStream endpoint requires `apiKey` credential'); } } if ((api === 'private') || (api === 'sapi') || (api === 'wapi' && path !== 'systemStatus') || (api === 'fapiPrivate')) { this.checkRequiredCredentials (); let query = undefined; if ((api === 'sapi') && (path === 'asset/dust')) { query = this.urlencodeWithArrayRepeat (this.extend ({ 'timestamp': this.nonce (), 'recvWindow': this.options['recvWindow'], }, params)); } else if (path === 'batchOrders') { query = this.rawencode (this.extend ({ 'timestamp': this.nonce (), 'recvWindow': this.options['recvWindow'], }, params)); } else { query = this.urlencode (this.extend ({ 'timestamp': this.nonce (), 'recvWindow': this.options['recvWindow'], }, params)); } const signature = this.hmac (this.encode (query), this.encode (this.secret)); query += '&' + 'signature=' + signature; headers = { 'X-MBX-APIKEY': this.apiKey, }; if ((method === 'GET') || (method === 'DELETE') || (api === 'wapi')) { url += '?' + query; } else { body = query; headers['Content-Type'] = 'application/x-www-form-urlencoded'; } } else { // userDataStream endpoints are public, but POST, PUT, DELETE // therefore they don't accept URL query arguments // https://github.com/ccxt/ccxt/issues/5224 if (!userDataStream) { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if ((code === 418) || (code === 429)) { throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); } // error response in a form: { "code": -1013, "msg": "Invalid quantity." } // following block cointains legacy checks against message patterns in "msg" property // will switch "code" checks eventually, when we know all of them if (code >= 400) { if (body.indexOf ('Price * QTY is zero or less') >= 0) { throw new InvalidOrder (this.id + ' order cost = amount * price is zero or less ' + body); } if (body.indexOf ('LOT_SIZE') >= 0) { throw new InvalidOrder (this.id + ' order amount should be evenly divisible by lot size ' + body); } if (body.indexOf ('PRICE_FILTER') >= 0) { throw new InvalidOrder (this.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid float value in general, use this.priceToPrecision (symbol, amount) ' + body); } } if (body.length > 0) { if (body[0] === '{') { // check success value for wapi endpoints // response in format {'msg': 'The coin does not exist.', 'success': true/false} const success = this.safeValue (response, 'success', true); if (!success) { const message = this.safeString (response, 'msg'); let parsedMessage = undefined; if (message !== undefined) { try { parsedMessage = JSON.parse (message); } catch (e) { // do nothing parsedMessage = undefined; } if (parsedMessage !== undefined) { response = parsedMessage; } } } const message = this.safeString (response, 'msg'); if (message !== undefined) { this.throwExactlyMatchedException (this.exceptions, message, this.id + ' ' + message); } // checks against error codes const error = this.safeString (response, 'code'); if (error !== undefined) { // https://github.com/ccxt/ccxt/issues/6501 if (error === '200') { return; } // a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} // despite that their message is very confusing, it is raised by Binance // on a temporary ban, the API key is valid, but disabled for a while if ((error === '-2015') && this.options['hasAlreadyAuthenticatedSuccessfully']) { throw new DDoSProtection (this.id + ' temporary banned: ' + body); } const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, error, feedback); throw new ExchangeError (feedback); } if (!success) { throw new ExchangeError (this.id + ' ' + body); } } } } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); // a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} if ((api === 'private') || (api === 'wapi')) { this.options['hasAlreadyAuthenticatedSuccessfully'] = true; } return response; } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],26:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const binance = require ('./binance.js'); // --------------------------------------------------------------------------- module.exports = class binanceje extends binance { describe () { return this.deepExtend (super.describe (), { 'id': 'binanceje', 'name': 'Binance Jersey', 'countries': [ 'JE' ], // Jersey 'certified': false, 'pro': true, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/54874009-d526eb00-4df3-11e9-928c-ce6a2b914cd1.jpg', 'api': { 'web': 'https://www.binance.je', 'wapi': 'https://api.binance.je/wapi/v3', 'public': 'https://api.binance.je/api/v1', 'private': 'https://api.binance.je/api/v3', 'v3': 'https://api.binance.je/api/v3', 'v1': 'https://api.binance.je/api/v1', }, 'www': 'https://www.binance.je', 'referral': 'https://www.binance.je/?ref=35047921', 'doc': 'https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md', 'fees': 'https://www.binance.je/fees.html', }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.0005, 'maker': 0.0005, }, // should be deleted, these are outdated and inaccurate 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BTC': 0.0005, 'ETH': 0.01, }, 'deposit': {}, }, }, }); } }; },{"./binance.js":25}],27:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const binance = require ('./binance.js'); // --------------------------------------------------------------------------- module.exports = class binanceus extends binance { describe () { return this.deepExtend (super.describe (), { 'id': 'binanceus', 'name': 'Binance US', 'countries': [ 'US' ], // US 'certified': false, 'pro': true, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/65177307-217b7c80-da5f-11e9-876e-0b748ba0a358.jpg', 'api': { 'web': 'https://www.binance.us', 'wapi': 'https://api.binance.us/wapi/v3', 'public': 'https://api.binance.us/api/v1', 'private': 'https://api.binance.us/api/v3', 'v3': 'https://api.binance.us/api/v3', 'v1': 'https://api.binance.us/api/v1', }, 'www': 'https://www.binance.us', 'referral': 'https://www.binance.us/?ref=35005074', 'doc': 'https://github.com/binance-us/binance-official-api-docs', 'fees': 'https://www.binance.us/en/fee/schedule', }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': 0.001, // 0.1% trading fee, zero fees for all trading pairs before November 1 'maker': 0.001, // 0.1% trading fee, zero fees for all trading pairs before November 1 }, }, }); } }; },{"./binance.js":25}],28:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ArgumentsRequired, ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bit2c extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bit2c', 'name': 'Bit2C', 'countries': [ 'IL' ], // Israel 'rateLimit': 3000, 'has': { 'CORS': false, 'fetchOpenOrders': true, 'fetchMyTrades': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766119-3593220e-5ece-11e7-8b3a-5a041f6bcc3f.jpg', 'api': 'https://bit2c.co.il', 'www': 'https://www.bit2c.co.il', 'referral': 'https://bit2c.co.il/Aff/63bfed10-e359-420c-ab5a-ad368dab0baf', 'doc': [ 'https://www.bit2c.co.il/home/api', 'https://github.com/OferE/bit2c', ], }, 'api': { 'public': { 'get': [ 'Exchanges/{pair}/Ticker', 'Exchanges/{pair}/orderbook', 'Exchanges/{pair}/trades', 'Exchanges/{pair}/lasttrades', ], }, 'private': { 'post': [ 'Merchant/CreateCheckout', 'Order/AddCoinFundsRequest', 'Order/AddFund', 'Order/AddOrder', 'Order/AddOrderMarketPriceBuy', 'Order/AddOrderMarketPriceSell', 'Order/CancelOrder', 'Order/AddCoinFundsRequest', 'Order/AddStopOrder', 'Payment/GetMyId', 'Payment/Send', 'Payment/Pay', ], 'get': [ 'Account/Balance', 'Account/Balance/v2', 'Order/MyOrders', 'Order/GetById', 'Order/AccountHistory', 'Order/OrderHistory', ], }, }, 'markets': { 'BTC/NIS': { 'id': 'BtcNis', 'symbol': 'BTC/NIS', 'base': 'BTC', 'quote': 'NIS', 'baseId': 'Btc', 'quoteId': 'Nis' }, 'ETH/NIS': { 'id': 'EthNis', 'symbol': 'ETH/NIS', 'base': 'ETH', 'quote': 'NIS', 'baseId': 'Eth', 'quoteId': 'Nis' }, 'BCH/NIS': { 'id': 'BchabcNis', 'symbol': 'BCH/NIS', 'base': 'BCH', 'quote': 'NIS', 'baseId': 'Bchabc', 'quoteId': 'Nis' }, 'LTC/NIS': { 'id': 'LtcNis', 'symbol': 'LTC/NIS', 'base': 'LTC', 'quote': 'NIS', 'baseId': 'Ltc', 'quoteId': 'Nis' }, 'ETC/NIS': { 'id': 'EtcNis', 'symbol': 'ETC/NIS', 'base': 'ETC', 'quote': 'NIS', 'baseId': 'Etc', 'quoteId': 'Nis' }, 'BTG/NIS': { 'id': 'BtgNis', 'symbol': 'BTG/NIS', 'base': 'BTG', 'quote': 'NIS', 'baseId': 'Btg', 'quoteId': 'Nis' }, 'BSV/NIS': { 'id': 'BchsvNis', 'symbol': 'BSV/NIS', 'base': 'BSV', 'quote': 'NIS', 'baseId': 'Bchsv', 'quoteId': 'Nis' }, 'GRIN/NIS': { 'id': 'GrinNis', 'symbol': 'GRIN/NIS', 'base': 'GRIN', 'quote': 'NIS', 'baseId': 'Grin', 'quoteId': 'Nis' }, }, 'fees': { 'trading': { 'maker': 0.5 / 100, 'taker': 0.5 / 100, }, }, 'options': { 'fetchTradesMethod': 'public_get_exchanges_pair_lasttrades', }, 'exceptions': { // { "error" : "Please provide valid APIkey" } // { "error" : "Please provide valid nonce in Request UInt64.TryParse failed for nonce :" } }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const balance = await this.privateGetAccountBalanceV2 (params); // // { // "AVAILABLE_NIS": 0.0, // "NIS": 0.0, // "LOCKED_NIS": 0.0, // "AVAILABLE_BTC": 0.0, // "BTC": 0.0, // "LOCKED_BTC": 0.0, // "AVAILABLE_ETH": 0.0, // "ETH": 0.0, // "LOCKED_ETH": 0.0, // "AVAILABLE_BCHSV": 0.0, // "BCHSV": 0.0, // "LOCKED_BCHSV": 0.0, // "AVAILABLE_BCHABC": 0.0, // "BCHABC": 0.0, // "LOCKED_BCHABC": 0.0, // "AVAILABLE_LTC": 0.0, // "LTC": 0.0, // "LOCKED_LTC": 0.0, // "AVAILABLE_ETC": 0.0, // "ETC": 0.0, // "LOCKED_ETC": 0.0, // "AVAILABLE_BTG": 0.0, // "BTG": 0.0, // "LOCKED_BTG": 0.0, // "AVAILABLE_GRIN": 0.0, // "GRIN": 0.0, // "LOCKED_GRIN": 0.0, // "Fees": { // "BtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, // "EthNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, // "BchabcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, // "LtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, // "EtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, // "BtgNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, // "LtcBtc": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, // "BchsvNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, // "GrinNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 } // } // } // const result = { 'info': balance }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const account = this.account (); const currencyId = this.currencyId (code); const uppercase = currencyId.toUpperCase (); if (uppercase in balance) { account['free'] = this.safeFloat (balance, 'AVAILABLE_' + uppercase); account['total'] = this.safeFloat (balance, uppercase); } result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; const orderbook = await this.publicGetExchangesPairOrderbook (this.extend (request, params)); return this.parseOrderBook (orderbook); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; const ticker = await this.publicGetExchangesPairTicker (this.extend (request, params)); const timestamp = this.milliseconds (); const averagePrice = this.safeFloat (ticker, 'av'); const baseVolume = this.safeFloat (ticker, 'a'); let quoteVolume = undefined; if (baseVolume !== undefined && averagePrice !== undefined) { quoteVolume = baseVolume * averagePrice; } const last = this.safeFloat (ticker, 'll'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'h'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'l'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': averagePrice, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const method = this.options['fetchTradesMethod']; const request = { 'pair': market['id'], }; const response = await this[method] (this.extend (request, params)); if (typeof response === 'string') { throw new ExchangeError (response); } return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); let method = 'privatePostOrderAddOrder'; const request = { 'Amount': amount, 'Pair': this.marketId (symbol), }; if (type === 'market') { method += 'MarketPrice' + this.capitalize (side); } else { request['Price'] = price; request['Total'] = amount * price; request['IsBid'] = (side === 'buy'); } const response = await this[method] (this.extend (request, params)); return { 'info': response, 'id': response['NewOrder']['id'], }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'id': id, }; return await this.privatePostOrderCancelOrder (this.extend (request, params)); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.privateGetOrderMyOrders (this.extend (request, params)); const orders = this.safeValue (response, market['id'], {}); const asks = this.safeValue (orders, 'ask', []); const bids = this.safeValue (orders, 'bid', []); return this.parseOrders (this.arrayConcat (asks, bids), market, since, limit); } parseOrder (order, market = undefined) { const timestamp = this.safeInteger (order, 'created'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } let side = this.safeValue (order, 'type'); if (side === 0) { side = 'buy'; } else if (side === 1) { side = 'sell'; } const id = this.safeString (order, 'id'); const status = this.safeString (order, 'status'); return { 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'filled': undefined, 'remaining': undefined, 'cost': cost, 'trades': undefined, 'fee': undefined, 'info': order, 'average': undefined, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (limit !== undefined) { request['take'] = limit; } request['take'] = limit; if (since !== undefined) { request['toTime'] = this.ymd (this.milliseconds (), '.'); request['fromTime'] = this.ymd (since, '.'); } if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } const response = await this.privateGetOrderOrderHistory (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseTrade (trade, market = undefined) { let timestamp = undefined; let id = undefined; let price = undefined; let amount = undefined; let orderId = undefined; let feeCost = undefined; let side = undefined; const reference = this.safeString (trade, 'reference'); if (reference !== undefined) { timestamp = this.safeTimestamp (trade, 'ticks'); price = this.safeFloat (trade, 'price'); amount = this.safeFloat (trade, 'firstAmount'); const reference_parts = reference.split ('|'); // reference contains 'pair|orderId|tradeId' if (market === undefined) { const marketId = this.safeString (trade, 'pair'); if (marketId in this.markets_by_id[marketId]) { market = this.markets_by_id[marketId]; } else if (reference_parts[0] in this.markets_by_id) { market = this.markets_by_id[reference_parts[0]]; } } orderId = reference_parts[1]; id = reference_parts[2]; side = this.safeInteger (trade, 'action'); if (side === 0) { side = 'buy'; } else if (side === 1) { side = 'sell'; } feeCost = this.safeFloat (trade, 'feeAmount'); } else { timestamp = this.safeTimestamp (trade, 'date'); id = this.safeString (trade, 'tid'); price = this.safeFloat (trade, 'price'); amount = this.safeFloat (trade, 'amount'); side = this.safeValue (trade, 'isBid'); if (side !== undefined) { if (side) { side = 'buy'; } else { side = 'sell'; } } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': price * amount, 'fee': { 'cost': feeCost, 'currency': 'NIS', 'rate': undefined, }, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.implodeParams (path, params); if (api === 'public') { url += '.json'; } else { this.checkRequiredCredentials (); const nonce = this.nonce (); const query = this.extend ({ 'nonce': nonce, }, params); const auth = this.urlencode (query); if (method === 'GET') { if (Object.keys (query).length) { url += '?' + auth; } } else { body = auth; } const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512', 'base64'); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'key': this.apiKey, 'sign': this.decode (signature), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],29:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, InvalidNonce, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitbank extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitbank', 'name': 'bitbank', 'countries': [ 'JP' ], 'version': 'v1', 'has': { 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchMyTrades': true, 'fetchDepositAddress': true, 'withdraw': true, }, 'timeframes': { '1m': '1min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '4h': '4hour', '8h': '8hour', '12h': '12hour', '1d': '1day', '1w': '1week', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/37808081-b87f2d9c-2e59-11e8-894d-c1900b7584fe.jpg', 'api': { 'public': 'https://public.bitbank.cc', 'private': 'https://api.bitbank.cc', }, 'www': 'https://bitbank.cc/', 'doc': 'https://docs.bitbank.cc/', 'fees': 'https://bitbank.cc/docs/fees/', }, 'api': { 'public': { 'get': [ '{pair}/ticker', '{pair}/depth', '{pair}/transactions', '{pair}/transactions/{yyyymmdd}', '{pair}/candlestick/{candletype}/{yyyymmdd}', ], }, 'private': { 'get': [ 'user/assets', 'user/spot/order', 'user/spot/active_orders', 'user/spot/trade_history', 'user/withdrawal_account', ], 'post': [ 'user/spot/order', 'user/spot/cancel_order', 'user/spot/cancel_orders', 'user/spot/orders_info', 'user/request_withdrawal', ], }, }, 'markets': { 'BCH/BTC': { 'id': 'bcc_btc', 'symbol': 'BCH/BTC', 'base': 'BCH', 'quote': 'BTC', 'baseId': 'bcc', 'quoteId': 'btc' }, 'BCH/JPY': { 'id': 'bcc_jpy', 'symbol': 'BCH/JPY', 'base': 'BCH', 'quote': 'JPY', 'baseId': 'bcc', 'quoteId': 'jpy' }, 'MONA/BTC': { 'id': 'mona_btc', 'symbol': 'MONA/BTC', 'base': 'MONA', 'quote': 'BTC', 'baseId': 'mona', 'quoteId': 'btc' }, 'MONA/JPY': { 'id': 'mona_jpy', 'symbol': 'MONA/JPY', 'base': 'MONA', 'quote': 'JPY', 'baseId': 'mona', 'quoteId': 'jpy' }, 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' }, 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, 'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' }, 'BTC/JPY': { 'id': 'btc_jpy', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy' }, }, 'fees': { 'trading': { 'maker': -0.02 / 100, 'taker': 0.12 / 100, }, 'funding': { 'withdraw': { // 'JPY': (amount > 30000) ? 756 : 540, 'BTC': 0.001, 'LTC': 0.001, 'XRP': 0.15, 'ETH': 0.0005, 'MONA': 0.001, 'BCC': 0.001, }, }, }, 'precision': { 'price': 8, 'amount': 8, }, 'exceptions': { '20001': AuthenticationError, '20002': AuthenticationError, '20003': AuthenticationError, '20005': AuthenticationError, '20004': InvalidNonce, '40020': InvalidOrder, '40021': InvalidOrder, '40025': ExchangeError, '40013': OrderNotFound, '40014': OrderNotFound, '50008': PermissionDenied, '50009': OrderNotFound, '50010': OrderNotFound, '60001': InsufficientFunds, '60005': InvalidOrder, }, }); } parseTicker (ticker, market = undefined) { let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger (ticker, 'timestamp'); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetPairTicker (this.extend (request, params)); return this.parseTicker (response['data'], market); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; const response = await this.publicGetPairDepth (this.extend (request, params)); const orderbook = this.safeValue (response, 'data', {}); const timestamp = this.safeInteger (orderbook, 'timestamp'); return this.parseOrderBook (orderbook, timestamp); } parseTrade (trade, market = undefined) { const timestamp = this.safeInteger (trade, 'executed_at'); let symbol = undefined; let feeCurrency = undefined; if (market !== undefined) { symbol = market['symbol']; feeCurrency = market['quote']; } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = parseFloat (this.costToPrecision (symbol, price * amount)); } } const id = this.safeString2 (trade, 'transaction_id', 'trade_id'); const takerOrMaker = this.safeString (trade, 'maker_taker'); let fee = undefined; const feeCost = this.safeFloat (trade, 'fee_amount_quote'); if (feeCost !== undefined) { fee = { 'currency': market['quote'], 'cost': feeCurrency, }; } const orderId = this.safeString (trade, 'order_id'); const type = this.safeString (trade, 'type'); const side = this.safeString (trade, 'side'); return { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': type, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetPairTransactions (this.extend (request, params)); return this.parseTrades (response['data']['transactions'], market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { return [ ohlcv[5], parseFloat (ohlcv[0]), parseFloat (ohlcv[1]), parseFloat (ohlcv[2]), parseFloat (ohlcv[3]), parseFloat (ohlcv[4]), ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let date = this.milliseconds (); date = this.ymd (date); date = date.split ('-'); const request = { 'pair': market['id'], 'candletype': this.timeframes[timeframe], 'yyyymmdd': date.join (''), }; const response = await this.publicGetPairCandlestickCandletypeYyyymmdd (this.extend (request, params)); const ohlcv = this.safeValue (response['data']['candlestick'][0], 'ohlcv'); return this.parseOHLCVs (ohlcv, market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetUserAssets (params); const result = { 'info': response }; const balances = response['data']['assets']; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'asset'); const code = this.safeCurrencyCode (currencyId); const account = { 'free': this.safeFloat (balance, 'free_amount'), 'used': this.safeFloat (balance, 'locked_amount'), 'total': this.safeFloat (balance, 'onhand_amount'), }; result[code] = account; } return this.parseBalance (result); } parseOrderStatus (status) { const statuses = { 'UNFILLED': 'open', 'PARTIALLY_FILLED': 'open', 'FULLY_FILLED': 'closed', 'CANCELED_UNFILLED': 'canceled', 'CANCELED_PARTIALLY_FILLED': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const id = this.safeString (order, 'order_id'); const marketId = this.safeString (order, 'pair'); let symbol = undefined; if (marketId && !market && (marketId in this.marketsById)) { market = this.marketsById[marketId]; } if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger (order, 'ordered_at'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'start_amount'); const filled = this.safeFloat (order, 'executed_amount'); const remaining = this.safeFloat (order, 'remaining_amount'); const average = this.safeFloat (order, 'average_price'); let cost = undefined; if (filled !== undefined) { if (average !== undefined) { cost = filled * average; } } const status = this.parseOrderStatus (this.safeString (order, 'status')); const type = this.safeStringLower (order, 'type'); const side = this.safeStringLower (order, 'side'); return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': undefined, 'info': order, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (price === undefined) { throw new InvalidOrder (this.id + ' createOrder requires a price argument for both market and limit orders'); } const request = { 'pair': market['id'], 'amount': this.amountToPrecision (symbol, amount), 'price': this.priceToPrecision (symbol, price), 'side': side, 'type': type, }; const response = await this.privatePostUserSpotOrder (this.extend (request, params)); const order = this.parseOrder (response['data'], market); const id = order['id']; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_id': id, 'pair': market['id'], }; const response = await this.privatePostUserSpotCancelOrder (this.extend (request, params)); return response['data']; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_id': id, 'pair': market['id'], }; const response = await this.privateGetUserSpotOrder (this.extend (request, params)); return this.parseOrder (response['data']); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['count'] = limit; } if (since !== undefined) { request['since'] = parseInt (since / 1000); } const response = await this.privateGetUserSpotActiveOrders (this.extend (request, params)); return this.parseOrders (response['data']['orders'], market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = {}; if (market !== undefined) { request['pair'] = market['id']; } if (limit !== undefined) { request['count'] = limit; } if (since !== undefined) { request['since'] = parseInt (since / 1000); } const response = await this.privateGetUserSpotTradeHistory (this.extend (request, params)); return this.parseTrades (response['data']['trades'], market, since, limit); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset': currency['id'], }; const response = await this.privateGetUserWithdrawalAccount (this.extend (request, params)); // Not sure about this if there could be more than one account... const accounts = response['data']['accounts']; const address = this.safeString (accounts[0], 'address'); return { 'currency': currency, 'address': address, 'tag': undefined, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { if (!('uuid' in params)) { throw new ExchangeError (this.id + ' uuid is required for withdrawal'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset': currency['id'], 'amount': amount, }; const response = await this.privatePostUserRequestWithdrawal (this.extend (request, params)); const txid = this.safeString (response['data'], 'txid'); return { 'info': response, 'id': txid, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let query = this.omit (params, this.extractParams (path)); let url = this.urls['api'][api] + '/'; if (api === 'public') { url += this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); let auth = nonce; url += this.version + '/' + this.implodeParams (path, params); if (method === 'POST') { body = this.json (query); auth += body; } else { auth += '/' + this.version + '/' + path; if (Object.keys (query).length) { query = this.urlencode (query); url += '?' + query; auth += '?' + query; } } headers = { 'Content-Type': 'application/json', 'ACCESS-KEY': this.apiKey, 'ACCESS-NONCE': nonce, 'ACCESS-SIGNATURE': this.hmac (this.encode (auth), this.encode (this.secret)), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); const success = this.safeInteger (response, 'success'); const data = this.safeValue (response, 'data'); if (!success || !data) { const errorMessages = { '10000': 'URL does not exist', '10001': 'A system error occurred. Please contact support', '10002': 'Invalid JSON format. Please check the contents of transmission', '10003': 'A system error occurred. Please contact support', '10005': 'A timeout error occurred. Please wait for a while and try again', '20001': 'API authentication failed', '20002': 'Illegal API key', '20003': 'API key does not exist', '20004': 'API Nonce does not exist', '20005': 'API signature does not exist', '20011': 'Two-step verification failed', '20014': 'SMS authentication failed', '30001': 'Please specify the order quantity', '30006': 'Please specify the order ID', '30007': 'Please specify the order ID array', '30009': 'Please specify the stock', '30012': 'Please specify the order price', '30013': 'Trade Please specify either', '30015': 'Please specify the order type', '30016': 'Please specify asset name', '30019': 'Please specify uuid', '30039': 'Please specify the amount to be withdrawn', '40001': 'The order quantity is invalid', '40006': 'Count value is invalid', '40007': 'End time is invalid', '40008': 'end_id Value is invalid', '40009': 'The from_id value is invalid', '40013': 'The order ID is invalid', '40014': 'The order ID array is invalid', '40015': 'Too many specified orders', '40017': 'Incorrect issue name', '40020': 'The order price is invalid', '40021': 'The trading classification is invalid', '40022': 'Start date is invalid', '40024': 'The order type is invalid', '40025': 'Incorrect asset name', '40028': 'uuid is invalid', '40048': 'The amount of withdrawal is illegal', '50003': 'Currently, this account is in a state where you can not perform the operation you specified. Please contact support', '50004': 'Currently, this account is temporarily registered. Please try again after registering your account', '50005': 'Currently, this account is locked. Please contact support', '50006': 'Currently, this account is locked. Please contact support', '50008': 'User identification has not been completed', '50009': 'Your order does not exist', '50010': 'Can not cancel specified order', '50011': 'API not found', '60001': 'The number of possessions is insufficient', '60002': 'It exceeds the quantity upper limit of the tender buying order', '60003': 'The specified quantity exceeds the limit', '60004': 'The specified quantity is below the threshold', '60005': 'The specified price is above the limit', '60006': 'The specified price is below the lower limit', '70001': 'A system error occurred. Please contact support', '70002': 'A system error occurred. Please contact support', '70003': 'A system error occurred. Please contact support', '70004': 'We are unable to accept orders as the transaction is currently suspended', '70005': 'Order can not be accepted because purchase order is currently suspended', '70006': 'We can not accept orders because we are currently unsubscribed ', '70009': 'We are currently temporarily restricting orders to be carried out. Please use the limit order.', '70010': 'We are temporarily raising the minimum order quantity as the system load is now rising.', }; const errorClasses = this.exceptions; const code = this.safeString (data, 'code'); const message = this.safeString (errorMessages, code, 'Error'); const ErrorClass = this.safeValue (errorClasses, code); if (ErrorClass !== undefined) { throw new ErrorClass (message); } else { throw new ExchangeError (this.id + ' ' + this.json (response)); } } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],30:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { InvalidNonce, InsufficientFunds, AuthenticationError, InvalidOrder, ExchangeError, OrderNotFound, AccountSuspended, BadSymbol, OrderImmediatelyFillable, RateLimitExceeded, OnMaintenance } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitbay extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitbay', 'name': 'BitBay', 'countries': [ 'MT', 'EU' ], // Malta 'rateLimit': 1000, 'has': { 'CORS': true, 'withdraw': true, 'fetchMyTrades': true, 'fetchOpenOrders': true, 'fetchOHLCV': true, }, 'timeframes': { '1m': '60', '3m': '180', '5m': '300', '15m': '900', '30m': '1800', '1h': '3600', '2h': '7200', '4h': '14400', '6h': '21600', '12h': '43200', '1d': '86400', '3d': '259200', '1w': '604800', }, 'urls': { 'referral': 'https://auth.bitbay.net/ref/jHlbB4mIkdS1', 'logo': 'https://user-images.githubusercontent.com/1294454/27766132-978a7bd8-5ece-11e7-9540-bc96d1e9bbb8.jpg', 'www': 'https://bitbay.net', 'api': { 'public': 'https://bitbay.net/API/Public', 'private': 'https://bitbay.net/API/Trading/tradingApi.php', 'v1_01Public': 'https://api.bitbay.net/rest', 'v1_01Private': 'https://api.bitbay.net/rest', }, 'doc': [ 'https://bitbay.net/public-api', 'https://bitbay.net/en/private-api', 'https://bitbay.net/account/tab-api', 'https://github.com/BitBayNet/API', 'https://docs.bitbay.net/v1.0.1-en/reference', ], 'support': 'https://support.bitbay.net', 'fees': 'https://bitbay.net/en/fees', }, 'api': { 'public': { 'get': [ '{id}/all', '{id}/market', '{id}/orderbook', '{id}/ticker', '{id}/trades', ], }, 'private': { 'post': [ 'info', 'trade', 'cancel', 'orderbook', 'orders', 'transfer', 'withdraw', 'history', 'transactions', ], }, 'v1_01Public': { 'get': [ 'trading/ticker', 'trading/ticker/{symbol}', 'trading/stats', 'trading/orderbook/{symbol}', 'trading/transactions/{symbol}', 'trading/candle/history/{symbol}/{resolution}', ], }, 'v1_01Private': { 'get': [ 'payments/withdrawal/{detailId}', 'payments/deposit/{detailId}', 'trading/offer', 'trading/config/{symbol}', 'trading/history/transactions', 'balances/BITBAY/history', 'balances/BITBAY/balance', 'fiat_cantor/rate/{baseId}/{quoteId}', 'fiat_cantor/history', ], 'post': [ 'trading/offer/{symbol}', 'trading/config/{symbol}', 'balances/BITBAY/balance', 'balances/BITBAY/balance/transfer/{source}/{destination}', 'fiat_cantor/exchange', ], 'delete': [ 'trading/offer/{symbol}/{id}/{side}/{price}', ], 'put': [ 'balances/BITBAY/balance/{id}', ], }, }, 'fees': { 'trading': { 'maker': 0.0, 'taker': 0.1 / 100, 'percentage': true, 'tierBased': false, }, 'fiat': { 'maker': 0.30 / 100, 'taker': 0.43 / 100, 'percentage': true, 'tierBased': true, 'tiers': { 'taker': [ [ 0.0043, 0 ], [ 0.0042, 1250 ], [ 0.0041, 3750 ], [ 0.0040, 7500 ], [ 0.0039, 10000 ], [ 0.0038, 15000 ], [ 0.0037, 20000 ], [ 0.0036, 25000 ], [ 0.0035, 37500 ], [ 0.0034, 50000 ], [ 0.0033, 75000 ], [ 0.0032, 100000 ], [ 0.0031, 150000 ], [ 0.0030, 200000 ], [ 0.0029, 250000 ], [ 0.0028, 375000 ], [ 0.0027, 500000 ], [ 0.0026, 625000 ], [ 0.0025, 875000 ], ], 'maker': [ [ 0.0030, 0 ], [ 0.0029, 1250 ], [ 0.0028, 3750 ], [ 0.0028, 7500 ], [ 0.0027, 10000 ], [ 0.0026, 15000 ], [ 0.0025, 20000 ], [ 0.0025, 25000 ], [ 0.0024, 37500 ], [ 0.0023, 50000 ], [ 0.0023, 75000 ], [ 0.0022, 100000 ], [ 0.0021, 150000 ], [ 0.0021, 200000 ], [ 0.0020, 250000 ], [ 0.0019, 375000 ], [ 0.0018, 500000 ], [ 0.0018, 625000 ], [ 0.0017, 875000 ], ], }, }, 'funding': { 'withdraw': { 'BTC': 0.0009, 'LTC': 0.005, 'ETH': 0.00126, 'LSK': 0.2, 'BCH': 0.0006, 'GAME': 0.005, 'DASH': 0.001, 'BTG': 0.0008, 'PLN': 4, 'EUR': 1.5, }, }, }, 'options': { 'fiatCurrencies': [ 'EUR', 'USD', 'GBP', 'PLN' ], }, 'exceptions': { '400': ExchangeError, // At least one parameter wasn't set '401': InvalidOrder, // Invalid order type '402': InvalidOrder, // No orders with specified currencies '403': InvalidOrder, // Invalid payment currency name '404': InvalidOrder, // Error. Wrong transaction type '405': InvalidOrder, // Order with this id doesn't exist '406': InsufficientFunds, // No enough money or crypto // code 407 not specified are not specified in their docs '408': InvalidOrder, // Invalid currency name '501': AuthenticationError, // Invalid public key '502': AuthenticationError, // Invalid sign '503': InvalidNonce, // Invalid moment parameter. Request time doesn't match current server time '504': ExchangeError, // Invalid method '505': AuthenticationError, // Key has no permission for this action '506': AccountSuspended, // Account locked. Please contact with customer service // codes 507 and 508 are not specified in their docs '509': ExchangeError, // The BIC/SWIFT is required for this currency '510': BadSymbol, // Invalid market name 'FUNDS_NOT_SUFFICIENT': InsufficientFunds, 'OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS': InvalidOrder, 'OFFER_NOT_FOUND': OrderNotFound, 'OFFER_WOULD_HAVE_BEEN_PARTIALLY_FILLED': OrderImmediatelyFillable, 'ACTION_LIMIT_EXCEEDED': RateLimitExceeded, 'UNDER_MAINTENANCE': OnMaintenance, }, }); } async fetchMarkets (params = {}) { const response = await this.v1_01PublicGetTradingTicker (params); const fiatCurrencies = this.safeValue (this.options, 'fiatCurrencies', []); // // { // status: 'Ok', // items: { // 'BSV-USD': { // market: { // code: 'BSV-USD', // first: { currency: 'BSV', minOffer: '0.00035', scale: 8 }, // second: { currency: 'USD', minOffer: '5', scale: 2 } // }, // time: '1557569762154', // highestBid: '52.31', // lowestAsk: '62.99', // rate: '63', // previousRate: '51.21', // }, // }, // } // const result = []; const items = this.safeValue (response, 'items'); const keys = Object.keys (items); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const item = items[key]; const market = this.safeValue (item, 'market', {}); const first = this.safeValue (market, 'first', {}); const second = this.safeValue (market, 'second', {}); const baseId = this.safeString (first, 'currency'); const quoteId = this.safeString (second, 'currency'); const id = baseId + quoteId; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (first, 'scale'), 'price': this.safeInteger (second, 'scale'), }; let fees = this.safeValue (this.fees, 'trading', {}); if (this.inArray (base, fiatCurrencies) || this.inArray (quote, fiatCurrencies)) { fees = this.safeValue (this.fees, 'fiat', {}); } const maker = this.safeFloat (fees, 'maker'); const taker = this.safeFloat (fees, 'taker'); // todo: check that the limits have ben interpreted correctly // todo: parse the fees page result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'active': undefined, 'maker': maker, 'taker': taker, 'limits': { 'amount': { 'min': this.safeFloat (first, 'minOffer'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeFloat (second, 'minOffer'), 'max': undefined, }, }, 'info': item, }); } return result; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; const response = await this.v1_01PrivateGetTradingOffer (this.extend (request, params)); const items = this.safeValue (response, 'items', []); return this.parseOrders (items, undefined, since, limit, { 'status': 'open' }); } parseOrder (order, market = undefined) { // // { // market: 'ETH-EUR', // offerType: 'Sell', // id: '93d3657b-d616-11e9-9248-0242ac110005', // currentAmount: '0.04', // lockedAmount: '0.04', // rate: '280', // startAmount: '0.04', // time: '1568372806924', // postOnly: false, // hidden: false, // mode: 'limit', // receivedAmount: '0.0', // firstBalanceId: '5b816c3e-437c-4e43-9bef-47814ae7ebfc', // secondBalanceId: 'ab43023b-4079-414c-b340-056e3430a3af' // } // const marketId = this.safeString (order, 'market'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const timestamp = this.safeInteger (order, 'time'); const amount = this.safeFloat (order, 'startAmount'); const remaining = this.safeFloat (order, 'currentAmount'); let filled = undefined; if (amount !== undefined) { if (remaining !== undefined) { filled = Math.max (0, amount - remaining); } } return { 'id': this.safeString (order, 'id'), 'clientOrderId': undefined, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': undefined, 'symbol': symbol, 'type': this.safeString (order, 'mode'), 'side': this.safeStringLower (order, 'offerType'), 'price': this.safeFloat (order, 'rate'), 'amount': amount, 'cost': undefined, 'filled': filled, 'remaining': remaining, 'average': undefined, 'fee': undefined, 'trades': undefined, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbol) { const markets = [ this.marketId (symbol) ]; request['markets'] = markets; } const query = { 'query': this.json (this.extend (request, params)) }; const response = await this.v1_01PrivateGetTradingHistoryTransactions (query); // // { // status: 'Ok', // totalRows: '67', // items: [ // { // id: 'b54659a0-51b5-42a0-80eb-2ac5357ccee2', // market: 'BTC-EUR', // time: '1541697096247', // amount: '0.00003', // rate: '4341.44', // initializedBy: 'Sell', // wasTaker: false, // userAction: 'Buy', // offerId: 'bd19804a-6f89-4a69-adb8-eb078900d006', // commissionValue: null // }, // ] // } // const items = this.safeValue (response, 'items'); const result = this.parseTrades (items, undefined, since, limit); if (symbol === undefined) { return result; } return this.filterBySymbol (result, symbol); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.v1_01PrivateGetBalancesBITBAYBalance (params); const balances = this.safeValue (response, 'balances'); if (balances === undefined) { throw new ExchangeError (this.id + ' empty balance response ' + this.json (response)); } const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['used'] = this.safeFloat (balance, 'lockedFunds'); account['free'] = this.safeFloat (balance, 'availableFunds'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const orderbook = await this.publicGetIdOrderbook (this.extend (request, params)); return this.parseOrderBook (orderbook); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const ticker = await this.publicGetIdTicker (this.extend (request, params)); const timestamp = this.milliseconds (); const baseVolume = this.safeFloat (ticker, 'volume'); const vwap = this.safeFloat (ticker, 'vwap'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'max'), 'low': this.safeFloat (ticker, 'min'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker, 'average'), 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { const balanceCurrencies = []; if (code !== undefined) { const currency = this.currency (code); balanceCurrencies.push (currency['id']); } let request = { 'balanceCurrencies': balanceCurrencies, }; if (since !== undefined) { request['fromTime'] = since; } if (limit !== undefined) { request['limit'] = limit; } request = this.extend (request, params); const response = await this.v1_01PrivateGetBalancesBITBAYHistory ({ 'query': this.json (request) }); const items = response['items']; return this.parseLedger (items, undefined, since, limit); } parseLedgerEntry (item, currency = undefined) { // // FUNDS_MIGRATION // { // "historyId": "84ea7a29-7da5-4de5-b0c0-871e83cad765", // "balance": { // "id": "821ec166-cb88-4521-916c-f4eb44db98df", // "currency": "LTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "LTC" // }, // "detailId": null, // "time": 1506128252968, // "type": "FUNDS_MIGRATION", // "value": 0.0009957, // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, // "fundsAfter": { "total": 0.0009957, "available": 0.0009957, "locked": 0 }, // "change": { "total": 0.0009957, "available": 0.0009957, "locked": 0 } // } // // CREATE_BALANCE // { // "historyId": "d0fabd8d-9107-4b5e-b9a6-3cab8af70d49", // "balance": { // "id": "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d", // "currency": "BTG", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTG" // }, // "detailId": null, // "time": 1508895244751, // "type": "CREATE_BALANCE", // "value": 0, // "fundsBefore": { "total": null, "available": null, "locked": null }, // "fundsAfter": { "total": 0, "available": 0, "locked": 0 }, // "change": { "total": 0, "available": 0, "locked": 0 } // } // // BITCOIN_GOLD_FORK // { // "historyId": "2b4d52d3-611c-473d-b92c-8a8d87a24e41", // "balance": { // "id": "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d", // "currency": "BTG", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTG" // }, // "detailId": null, // "time": 1508895244778, // "type": "BITCOIN_GOLD_FORK", // "value": 0.00453512, // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, // "fundsAfter": { "total": 0.00453512, "available": 0.00453512, "locked": 0 }, // "change": { "total": 0.00453512, "available": 0.00453512, "locked": 0 } // } // // ADD_FUNDS // { // "historyId": "3158236d-dae5-4a5d-81af-c1fa4af340fb", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "8e83a960-e737-4380-b8bb-259d6e236faa", // "time": 1520631178816, // "type": "ADD_FUNDS", // "value": 0.628405, // "fundsBefore": { "total": 0.00453512, "available": 0.00453512, "locked": 0 }, // "fundsAfter": { "total": 0.63294012, "available": 0.63294012, "locked": 0 }, // "change": { "total": 0.628405, "available": 0.628405, "locked": 0 } // } // // TRANSACTION_PRE_LOCKING // { // "historyId": "e7d19e0f-03b3-46a8-bc72-dde72cc24ead", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": null, // "time": 1520706403868, // "type": "TRANSACTION_PRE_LOCKING", // "value": -0.1, // "fundsBefore": { "total": 0.63294012, "available": 0.63294012, "locked": 0 }, // "fundsAfter": { "total": 0.63294012, "available": 0.53294012, "locked": 0.1 }, // "change": { "total": 0, "available": -0.1, "locked": 0.1 } // } // // TRANSACTION_POST_OUTCOME // { // "historyId": "c4010825-231d-4a9c-8e46-37cde1f7b63c", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "bf2876bc-b545-4503-96c8-ef4de8233876", // "time": 1520706404032, // "type": "TRANSACTION_POST_OUTCOME", // "value": -0.01771415, // "fundsBefore": { "total": 0.63294012, "available": 0.53294012, "locked": 0.1 }, // "fundsAfter": { "total": 0.61522597, "available": 0.53294012, "locked": 0.08228585 }, // "change": { "total": -0.01771415, "available": 0, "locked": -0.01771415 } // } // // TRANSACTION_POST_INCOME // { // "historyId": "7f18b7af-b676-4125-84fd-042e683046f6", // "balance": { // "id": "ab43023b-4079-414c-b340-056e3430a3af", // "currency": "EUR", // "type": "FIAT", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "EUR" // }, // "detailId": "f5fcb274-0cc7-4385-b2d3-bae2756e701f", // "time": 1520706404035, // "type": "TRANSACTION_POST_INCOME", // "value": 628.78, // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, // "fundsAfter": { "total": 628.78, "available": 628.78, "locked": 0 }, // "change": { "total": 628.78, "available": 628.78, "locked": 0 } // } // // TRANSACTION_COMMISSION_OUTCOME // { // "historyId": "843177fa-61bc-4cbf-8be5-b029d856c93b", // "balance": { // "id": "ab43023b-4079-414c-b340-056e3430a3af", // "currency": "EUR", // "type": "FIAT", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "EUR" // }, // "detailId": "f5fcb274-0cc7-4385-b2d3-bae2756e701f", // "time": 1520706404050, // "type": "TRANSACTION_COMMISSION_OUTCOME", // "value": -2.71, // "fundsBefore": { "total": 766.06, "available": 766.06, "locked": 0 }, // "fundsAfter": { "total": 763.35,"available": 763.35, "locked": 0 }, // "change": { "total": -2.71, "available": -2.71, "locked": 0 } // } // // TRANSACTION_OFFER_COMPLETED_RETURN // { // "historyId": "cac69b04-c518-4dc5-9d86-e76e91f2e1d2", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": null, // "time": 1520714886425, // "type": "TRANSACTION_OFFER_COMPLETED_RETURN", // "value": 0.00000196, // "fundsBefore": { "total": 0.00941208, "available": 0.00941012, "locked": 0.00000196 }, // "fundsAfter": { "total": 0.00941208, "available": 0.00941208, "locked": 0 }, // "change": { "total": 0, "available": 0.00000196, "locked": -0.00000196 } // } // // WITHDRAWAL_LOCK_FUNDS // { // "historyId": "03de2271-66ab-4960-a786-87ab9551fc14", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6", // "time": 1522245654481, // "type": "WITHDRAWAL_LOCK_FUNDS", // "value": -0.8, // "fundsBefore": { "total": 0.8, "available": 0.8, "locked": 0 }, // "fundsAfter": { "total": 0.8, "available": 0, "locked": 0.8 }, // "change": { "total": 0, "available": -0.8, "locked": 0.8 } // } // // WITHDRAWAL_SUBTRACT_FUNDS // { // "historyId": "b0308c89-5288-438d-a306-c6448b1a266d", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6", // "time": 1522246526186, // "type": "WITHDRAWAL_SUBTRACT_FUNDS", // "value": -0.8, // "fundsBefore": { "total": 0.8, "available": 0, "locked": 0.8 }, // "fundsAfter": { "total": 0, "available": 0, "locked": 0 }, // "change": { "total": -0.8, "available": 0, "locked": -0.8 } // } // // TRANSACTION_OFFER_ABORTED_RETURN // { // "historyId": "b1a3c075-d403-4e05-8f32-40512cdd88c0", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": null, // "time": 1522512298662, // "type": "TRANSACTION_OFFER_ABORTED_RETURN", // "value": 0.0564931, // "fundsBefore": { "total": 0.44951311, "available": 0.39302001, "locked": 0.0564931 }, // "fundsAfter": { "total": 0.44951311, "available": 0.44951311, "locked": 0 }, // "change": { "total": 0, "available": 0.0564931, "locked": -0.0564931 } // } // // WITHDRAWAL_UNLOCK_FUNDS // { // "historyId": "0ed569a2-c330-482e-bb89-4cb553fb5b11", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "0c7be256-c336-4111-bee7-4eb22e339700", // "time": 1527866360785, // "type": "WITHDRAWAL_UNLOCK_FUNDS", // "value": 0.05045, // "fundsBefore": { "total": 0.86001578, "available": 0.80956578, "locked": 0.05045 }, // "fundsAfter": { "total": 0.86001578, "available": 0.86001578, "locked": 0 }, // "change": { "total": 0, "available": 0.05045, "locked": -0.05045 } // } // // TRANSACTION_COMMISSION_RETURN // { // "historyId": "07c89c27-46f1-4d7a-8518-b73798bf168a", // "balance": { // "id": "ab43023b-4079-414c-b340-056e3430a3af", // "currency": "EUR", // "type": "FIAT", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "EUR" // }, // "detailId": null, // "time": 1528304043063, // "type": "TRANSACTION_COMMISSION_RETURN", // "value": 0.6, // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, // "fundsAfter": { "total": 0.6, "available": 0.6, "locked": 0 }, // "change": { "total": 0.6, "available": 0.6, "locked": 0 } // } // const timestamp = this.safeInteger (item, 'time'); const balance = this.safeValue (item, 'balance', {}); const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const change = this.safeValue (item, 'change', {}); let amount = this.safeFloat (change, 'total'); let direction = 'in'; if (amount < 0) { direction = 'out'; amount = -amount; } const id = this.safeString (item, 'historyId'); // there are 2 undocumented api calls: (v1_01PrivateGetPaymentsDepositDetailId and v1_01PrivateGetPaymentsWithdrawalDetailId) // that can be used to enrich the transfers with txid, address etc (you need to use info.detailId as a parameter) const referenceId = this.safeString (item, 'detailId'); const type = this.parseLedgerEntryType (this.safeString (item, 'type')); const fundsBefore = this.safeValue (item, 'fundsBefore', {}); const before = this.safeFloat (fundsBefore, 'total'); const fundsAfter = this.safeValue (item, 'fundsAfter', {}); const after = this.safeFloat (fundsAfter, 'total'); return { 'info': item, 'id': id, 'direction': direction, 'account': undefined, 'referenceId': referenceId, 'referenceAccount': undefined, 'type': type, 'currency': code, 'amount': amount, 'before': before, 'after': after, 'status': 'ok', 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': undefined, }; } parseLedgerEntryType (type) { const types = { 'ADD_FUNDS': 'transaction', 'BITCOIN_GOLD_FORK': 'transaction', 'CREATE_BALANCE': 'transaction', 'FUNDS_MIGRATION': 'transaction', 'WITHDRAWAL_LOCK_FUNDS': 'transaction', 'WITHDRAWAL_SUBTRACT_FUNDS': 'transaction', 'WITHDRAWAL_UNLOCK_FUNDS': 'transaction', 'TRANSACTION_COMMISSION_OUTCOME': 'fee', 'TRANSACTION_COMMISSION_RETURN': 'fee', 'TRANSACTION_OFFER_ABORTED_RETURN': 'trade', 'TRANSACTION_OFFER_COMPLETED_RETURN': 'trade', 'TRANSACTION_POST_INCOME': 'trade', 'TRANSACTION_POST_OUTCOME': 'trade', 'TRANSACTION_PRE_LOCKING': 'trade', }; return this.safeString (types, type, type); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // [ // '1582399800000', // { // o: '0.0001428', // c: '0.0001428', // h: '0.0001428', // l: '0.0001428', // v: '4', // co: '1' // } // ] return [ parseInt (ohlcv[0]), this.safeFloat (ohlcv[1], 'o'), this.safeFloat (ohlcv[1], 'h'), this.safeFloat (ohlcv[1], 'l'), this.safeFloat (ohlcv[1], 'c'), this.safeFloat (ohlcv[1], 'v'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const tradingSymbol = market['baseId'] + '-' + market['quoteId']; const request = { 'symbol': tradingSymbol, 'resolution': this.timeframes[timeframe], // 'from': 1574709092000, // unix timestamp in milliseconds, required // 'to': 1574709092000, // unix timestamp in milliseconds, required }; if (limit === undefined) { limit = 100; } const duration = this.parseTimeframe (timeframe); const timerange = limit * duration * 1000; if (since === undefined) { request['to'] = this.milliseconds (); request['from'] = request['to'] - timerange; } else { request['from'] = parseInt (since); request['to'] = this.sum (request['from'], timerange); } const response = await this.v1_01PublicGetTradingCandleHistorySymbolResolution (this.extend (request, params)); const ohlcvs = this.safeValue (response, 'items', []); return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // createOrder trades // // { // "rate": "0.02195928", // "amount": "0.00167952" // } // // fetchMyTrades (private) // // { // amount: "0.29285199", // commissionValue: "0.00125927", // id: "11c8203a-a267-11e9-b698-0242ac110007", // initializedBy: "Buy", // market: "ETH-EUR", // offerId: "11c82038-a267-11e9-b698-0242ac110007", // rate: "277", // time: "1562689917517", // userAction: "Buy", // wasTaker: true, // } // // fetchTrades (public) // // { // id: 'df00b0da-e5e0-11e9-8c19-0242ac11000a', // t: '1570108958831', // a: '0.04776653', // r: '0.02145854', // ty: 'Sell' // } // const timestamp = this.safeInteger2 (trade, 'time', 't'); const userAction = this.safeString (trade, 'userAction'); const side = (userAction === 'Buy') ? 'buy' : 'sell'; const wasTaker = this.safeValue (trade, 'wasTaker'); let takerOrMaker = undefined; if (wasTaker !== undefined) { takerOrMaker = wasTaker ? 'taker' : 'maker'; } const price = this.safeFloat2 (trade, 'rate', 'r'); const amount = this.safeFloat2 (trade, 'amount', 'a'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } const feeCost = this.safeFloat (trade, 'commissionValue'); const marketId = this.safeString (trade, 'market'); let base = undefined; let quote = undefined; let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; base = market['base']; quote = market['quote']; } else { const [ baseId, quoteId ] = marketId.split ('-'); base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (market !== undefined) { if (symbol === undefined) { symbol = market['symbol']; } if (base === undefined) { base = market['base']; } } let fee = undefined; if (feeCost !== undefined) { const feeCcy = (side === 'buy') ? base : quote; fee = { 'currency': feeCcy, 'cost': feeCost, }; } const order = this.safeString (trade, 'offerId'); // todo: check this logic let type = undefined; if (order !== undefined) { type = order ? 'limit' : 'market'; } return { 'id': this.safeString (trade, 'id'), 'order': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'takerOrMaker': takerOrMaker, 'fee': fee, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const tradingSymbol = market['baseId'] + '-' + market['quoteId']; const request = { 'symbol': tradingSymbol, }; if (since !== undefined) { request['fromTime'] = since - 1; // result does not include exactly `since` time therefore decrease by 1 } if (limit !== undefined) { request['limit'] = limit; // default - 10, max - 300 } const response = await this.v1_01PublicGetTradingTransactionsSymbol (this.extend (request, params)); const items = this.safeValue (response, 'items'); return this.parseTrades (items, symbol, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const tradingSymbol = market['baseId'] + '-' + market['quoteId']; const request = { 'symbol': tradingSymbol, 'offerType': side, 'amount': amount, 'mode': type, }; if (type === 'limit') { request['rate'] = price; } const response = await this.v1_01PrivatePostTradingOfferSymbol (this.extend (request, params)); // // unfilled (open order) // // { // status: 'Ok', // completed: false, // can deduce status from here // offerId: 'ce9cc72e-d61c-11e9-9248-0242ac110005', // transactions: [], // can deduce order info from here // } // // filled (closed order) // // { // "status": "Ok", // "offerId": "942a4a3e-e922-11e9-8c19-0242ac11000a", // "completed": true, // "transactions": [ // { // "rate": "0.02195928", // "amount": "0.00167952" // }, // { // "rate": "0.02195928", // "amount": "0.00167952" // }, // { // "rate": "0.02196207", // "amount": "0.27704177" // } // ] // } // // partially-filled (open order) // // { // "status": "Ok", // "offerId": "d0ebefab-f4d7-11e9-8c19-0242ac11000a", // "completed": false, // "transactions": [ // { // "rate": "0.02106404", // "amount": "0.0019625" // }, // { // "rate": "0.02106404", // "amount": "0.0019625" // }, // { // "rate": "0.02105901", // "amount": "0.00975256" // } // ] // } // const timestamp = this.milliseconds (); // the real timestamp is missing in the response const id = this.safeString (response, 'offerId'); const completed = this.safeValue (response, 'completed', false); const status = completed ? 'closed' : 'open'; let filled = 0; let cost = undefined; const transactions = this.safeValue (response, 'transactions'); let trades = undefined; if (transactions !== undefined) { trades = this.parseTrades (transactions, market, undefined, undefined, { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'side': side, 'type': type, 'orderId': id, }); cost = 0; for (let i = 0; i < trades.length; i++) { filled = this.sum (filled, trades[i]['amount']); cost = this.sum (cost, trades[i]['cost']); } } const remaining = amount - filled; return { 'id': id, 'info': response, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': parseFloat (price), 'amount': parseFloat (amount), 'cost': cost, 'filled': filled, 'remaining': remaining, 'average': undefined, 'fee': undefined, 'trades': trades, 'clientOrderId': undefined, }; } async cancelOrder (id, symbol = undefined, params = {}) { const side = this.safeString (params, 'side'); if (side === undefined) { throw new ExchangeError (this.id + ' cancelOrder() requires a `side` parameter ("buy" or "sell")'); } const price = this.safeValue (params, 'price'); if (price === undefined) { throw new ExchangeError (this.id + ' cancelOrder() requires a `price` parameter (float or string)'); } await this.loadMarkets (); const market = this.market (symbol); const tradingSymbol = market['baseId'] + '-' + market['quoteId']; const request = { 'symbol': tradingSymbol, 'id': id, 'side': side, 'price': price, }; // { status: 'Fail', errors: [ 'NOT_RECOGNIZED_OFFER_TYPE' ] } -- if required params are missing // { status: 'Ok', errors: [] } return this.v1_01PrivateDeleteTradingOfferSymbolIdSidePrice (this.extend (request, params)); } isFiat (currency) { const fiatCurrencies = { 'USD': true, 'EUR': true, 'PLN': true, }; return this.safeValue (fiatCurrencies, currency, false); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); let method = undefined; const currency = this.currency (code); const request = { 'currency': currency['id'], 'quantity': amount, }; if (this.isFiat (code)) { method = 'privatePostWithdraw'; // request['account'] = params['account']; // they demand an account number // request['express'] = params['express']; // whatever it means, they don't explain // request['bic'] = ''; } else { method = 'privatePostTransfer'; if (tag !== undefined) { address += '?dt=' + tag.toString (); } request['address'] = address; } const response = await this[method] (this.extend (request, params)); return { 'info': response, 'id': undefined, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; if (api === 'public') { const query = this.omit (params, this.extractParams (path)); url += '/' + this.implodeParams (path, params) + '.json'; if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'v1_01Public') { const query = this.omit (params, this.extractParams (path)); url += '/' + this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'v1_01Private') { this.checkRequiredCredentials (); const query = this.omit (params, this.extractParams (path)); url += '/' + this.implodeParams (path, params); const nonce = this.milliseconds ().toString (); let payload = undefined; if (method !== 'POST') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } payload = this.apiKey + nonce; } else if (body === undefined) { body = this.json (query); payload = this.apiKey + nonce + body; } headers = { 'Request-Timestamp': nonce, 'Operation-Id': this.uuid (), 'API-Key': this.apiKey, 'API-Hash': this.hmac (this.encode (payload), this.encode (this.secret), 'sha512'), 'Content-Type': 'application/json', }; } else { this.checkRequiredCredentials (); body = this.urlencode (this.extend ({ 'method': path, 'moment': this.nonce (), }, params)); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'API-Key': this.apiKey, 'API-Hash': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('code' in response) { // // bitbay returns the integer 'success': 1 key from their private API // or an integer 'code' value from 0 to 510 and an error message // // { 'success': 1, ... } // { 'code': 502, 'message': 'Invalid sign' } // { 'code': 0, 'message': 'offer funds not exceeding minimums' } // // 400 At least one parameter wasn't set // 401 Invalid order type // 402 No orders with specified currencies // 403 Invalid payment currency name // 404 Error. Wrong transaction type // 405 Order with this id doesn't exist // 406 No enough money or crypto // 408 Invalid currency name // 501 Invalid public key // 502 Invalid sign // 503 Invalid moment parameter. Request time doesn't match current server time // 504 Invalid method // 505 Key has no permission for this action // 506 Account locked. Please contact with customer service // 509 The BIC/SWIFT is required for this currency // 510 Invalid market name // const code = this.safeString (response, 'code'); // always an integer const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, code, feedback); throw new ExchangeError (feedback); } else if ('status' in response) { // // {"status":"Fail","errors":["OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS"]} // const status = this.safeString (response, 'status'); if (status === 'Fail') { const errors = this.safeValue (response, 'errors'); const feedback = this.id + ' ' + body; for (let i = 0; i < errors.length; i++) { const error = errors[i]; this.throwExactlyMatchedException (this.exceptions, error, feedback); } throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9}],31:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { NotSupported, RateLimitExceeded, AuthenticationError, PermissionDenied, ArgumentsRequired, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, InvalidNonce } = require ('./base/errors'); const { SIGNIFICANT_DIGITS } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class bitfinex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitfinex', 'name': 'Bitfinex', 'countries': [ 'VG' ], 'version': 'v1', 'rateLimit': 1500, 'certified': true, 'pro': true, // new metainfo interface 'has': { 'CORS': false, 'cancelAllOrders': true, 'createDepositAddress': true, 'deposit': true, 'fetchClosedOrders': true, 'fetchDepositAddress': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'fetchFundingFees': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchTickers': true, 'fetchTransactions': true, 'fetchDeposits': false, 'fetchWithdrawals': false, 'withdraw': true, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '3h': '3h', '6h': '6h', '12h': '12h', '1d': '1D', '1w': '7D', '2w': '14D', '1M': '1M', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg', 'api': { 'v2': 'https://api-pub.bitfinex.com', // https://github.com/ccxt/ccxt/issues/5109 'public': 'https://api.bitfinex.com', 'private': 'https://api.bitfinex.com', }, 'www': 'https://www.bitfinex.com', 'referral': 'https://www.bitfinex.com/?refcode=P61eYxFL', 'doc': [ 'https://docs.bitfinex.com/v1/docs', 'https://github.com/bitfinexcom/bitfinex-api-node', ], }, 'api': { // v2 symbol ids require a 't' prefix // just the public part of it (use bitfinex2 for everything else) 'v2': { 'get': [ 'platform/status', 'tickers', 'ticker/{symbol}', 'trades/{symbol}/hist', 'book/{symbol}/{precision}', 'book/{symbol}/P0', 'book/{symbol}/P1', 'book/{symbol}/P2', 'book/{symbol}/P3', 'book/{symbol}/R0', 'stats1/{key}:{size}:{symbol}:{side}/{section}', 'stats1/{key}:{size}:{symbol}/{section}', 'stats1/{key}:{size}:{symbol}:long/last', 'stats1/{key}:{size}:{symbol}:long/hist', 'stats1/{key}:{size}:{symbol}:short/last', 'stats1/{key}:{size}:{symbol}:short/hist', 'candles/trade:{timeframe}:{symbol}/{section}', 'candles/trade:{timeframe}:{symbol}/last', 'candles/trade:{timeframe}:{symbol}/hist', ], }, 'public': { 'get': [ 'book/{symbol}', // 'candles/{symbol}', 'lendbook/{currency}', 'lends/{currency}', 'pubticker/{symbol}', 'stats/{symbol}', 'symbols', 'symbols_details', 'tickers', 'trades/{symbol}', ], }, 'private': { 'post': [ 'account_fees', 'account_infos', 'balances', 'basket_manage', 'credits', 'deposit/new', 'funding/close', 'history', 'history/movements', 'key_info', 'margin_infos', 'mytrades', 'mytrades_funding', 'offer/cancel', 'offer/new', 'offer/status', 'offers', 'offers/hist', 'order/cancel', 'order/cancel/all', 'order/cancel/multi', 'order/cancel/replace', 'order/new', 'order/new/multi', 'order/status', 'orders', 'orders/hist', 'position/claim', 'position/close', 'positions', 'summary', 'taken_funds', 'total_taken_funds', 'transfer', 'unused_taken_funds', 'withdraw', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'maker': 0.1 / 100, 'taker': 0.2 / 100, 'tiers': { 'taker': [ [0, 0.2 / 100], [500000, 0.2 / 100], [1000000, 0.2 / 100], [2500000, 0.2 / 100], [5000000, 0.2 / 100], [7500000, 0.2 / 100], [10000000, 0.18 / 100], [15000000, 0.16 / 100], [20000000, 0.14 / 100], [25000000, 0.12 / 100], [30000000, 0.1 / 100], ], 'maker': [ [0, 0.1 / 100], [500000, 0.08 / 100], [1000000, 0.06 / 100], [2500000, 0.04 / 100], [5000000, 0.02 / 100], [7500000, 0], [10000000, 0], [15000000, 0], [20000000, 0], [25000000, 0], [30000000, 0], ], }, }, 'funding': { 'tierBased': false, // true for tier-based/progressive 'percentage': false, // fixed commission // Actually deposit fees are free for larger deposits (> $1000 USD equivalent) // these values below are deprecated, we should not hardcode fees and limits anymore // to be reimplemented with bitfinex funding fees from their API or web endpoints 'deposit': { 'BTC': 0.0004, 'IOTA': 0.5, 'ETH': 0.0027, 'BCH': 0.0001, 'LTC': 0.001, 'EOS': 0.24279, 'XMR': 0.04, 'SAN': 0.99269, 'DASH': 0.01, 'ETC': 0.01, 'XRP': 0.02, 'YYW': 16.915, 'NEO': 0, 'ZEC': 0.001, 'BTG': 0, 'OMG': 0.14026, 'DATA': 20.773, 'QASH': 1.9858, 'ETP': 0.01, 'QTUM': 0.01, 'EDO': 0.95001, 'AVT': 1.3045, 'USDT': 0, 'TRX': 28.184, 'ZRX': 1.9947, 'RCN': 10.793, 'TNB': 31.915, 'SNT': 14.976, 'RLC': 1.414, 'GNT': 5.8952, 'SPK': 10.893, 'REP': 0.041168, 'BAT': 6.1546, 'ELF': 1.8753, 'FUN': 32.336, 'SNG': 18.622, 'AID': 8.08, 'MNA': 16.617, 'NEC': 1.6504, 'XTZ': 0.2, }, 'withdraw': { 'BTC': 0.0004, 'IOTA': 0.5, 'ETH': 0.0027, 'BCH': 0.0001, 'LTC': 0.001, 'EOS': 0.24279, 'XMR': 0.04, 'SAN': 0.99269, 'DASH': 0.01, 'ETC': 0.01, 'XRP': 0.02, 'YYW': 16.915, 'NEO': 0, 'ZEC': 0.001, 'BTG': 0, 'OMG': 0.14026, 'DATA': 20.773, 'QASH': 1.9858, 'ETP': 0.01, 'QTUM': 0.01, 'EDO': 0.95001, 'AVT': 1.3045, 'USDT': 20, 'TRX': 28.184, 'ZRX': 1.9947, 'RCN': 10.793, 'TNB': 31.915, 'SNT': 14.976, 'RLC': 1.414, 'GNT': 5.8952, 'SPK': 10.893, 'REP': 0.041168, 'BAT': 6.1546, 'ELF': 1.8753, 'FUN': 32.336, 'SNG': 18.622, 'AID': 8.08, 'MNA': 16.617, 'NEC': 1.6504, 'XTZ': 0.2, }, }, }, // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method 'commonCurrencies': { 'ABS': 'ABYSS', 'AIO': 'AION', 'ALG': 'ALGO', // https://github.com/ccxt/ccxt/issues/6034 'AMP': 'AMPL', 'ATM': 'ATMI', 'ATO': 'ATOM', // https://github.com/ccxt/ccxt/issues/5118 'BAB': 'BCH', 'CTX': 'CTXC', 'DAD': 'DADI', 'DAT': 'DATA', 'DSH': 'DASH', 'DRK': 'DRK', 'GSD': 'GUSD', 'HOT': 'Hydro Protocol', 'IOS': 'IOST', 'IOT': 'IOTA', 'IQX': 'IQ', 'MIT': 'MITH', 'MNA': 'MANA', 'NCA': 'NCASH', 'ORS': 'ORS Group', // conflict with Origin Sport #3230 'POY': 'POLY', 'QSH': 'QASH', 'QTM': 'QTUM', 'SEE': 'SEER', 'SNG': 'SNGLS', 'SPK': 'SPANK', 'STJ': 'STORJ', 'TSD': 'TUSD', 'YYW': 'YOYOW', 'UDC': 'USDC', 'UST': 'USDT', 'UTN': 'UTNP', 'VSY': 'VSYS', 'WAX': 'WAXP', 'XCH': 'XCHF', 'ZBT': 'ZB', }, 'exceptions': { 'exact': { 'temporarily_unavailable': ExchangeNotAvailable, // Sorry, the service is temporarily unavailable. See https://www.bitfinex.com/ for more info. 'Order could not be cancelled.': OrderNotFound, // non-existent order 'No such order found.': OrderNotFound, // ? 'Order price must be positive.': InvalidOrder, // on price <= 0 'Could not find a key matching the given X-BFX-APIKEY.': AuthenticationError, 'Key price should be a decimal number, e.g. "123.456"': InvalidOrder, // on isNaN (price) 'Key amount should be a decimal number, e.g. "123.456"': InvalidOrder, // on isNaN (amount) 'ERR_RATE_LIMIT': RateLimitExceeded, 'Ratelimit': RateLimitExceeded, 'Nonce is too small.': InvalidNonce, 'No summary found.': ExchangeError, // fetchTradingFees (summary) endpoint can give this vague error message 'Cannot evaluate your available balance, please try again': ExchangeNotAvailable, }, 'broad': { 'This API key does not have permission': PermissionDenied, // authenticated but not authorized 'not enough exchange balance for ': InsufficientFunds, // when buying cost is greater than the available quote currency 'minimum size for ': InvalidOrder, // when amount below limits.amount.min 'Invalid order': InvalidOrder, // ? 'The available balance is only': InsufficientFunds, // {"status":"error","message":"Cannot withdraw 1.0027 ETH from your exchange wallet. The available balance is only 0.0 ETH. If you have limit orders, open positions, unused or active margin funding, this will decrease your available balance. To increase it, you can cancel limit orders or reduce/close your positions.","withdrawal_id":0,"fees":"0.0027"} }, }, 'precisionMode': SIGNIFICANT_DIGITS, 'options': { 'currencyNames': { 'AGI': 'agi', 'AID': 'aid', 'AIO': 'aio', 'ANT': 'ant', 'AVT': 'aventus', // #1811 'BAT': 'bat', // https://github.com/ccxt/ccxt/issues/5833 'BCH': 'bab', // undocumented // 'BCH': 'bcash', // undocumented 'BCI': 'bci', 'BFT': 'bft', 'BTC': 'bitcoin', 'BTG': 'bgold', 'CFI': 'cfi', 'DAI': 'dai', 'DADI': 'dad', 'DASH': 'dash', 'DATA': 'datacoin', 'DTH': 'dth', 'EDO': 'eidoo', // #1811 'ELF': 'elf', 'EOS': 'eos', 'ETC': 'ethereumc', 'ETH': 'ethereum', 'ETP': 'metaverse', 'FUN': 'fun', 'GNT': 'golem', 'IOST': 'ios', 'IOTA': 'iota', // https://github.com/ccxt/ccxt/issues/5833 'LEO': 'let', // ETH chain // 'LEO': 'les', // EOS chain 'LRC': 'lrc', 'LTC': 'litecoin', 'LYM': 'lym', 'MANA': 'mna', 'MIT': 'mit', 'MKR': 'mkr', 'MTN': 'mtn', 'NEO': 'neo', 'ODE': 'ode', 'OMG': 'omisego', 'OMNI': 'mastercoin', 'QASH': 'qash', 'QTUM': 'qtum', // #1811 'RCN': 'rcn', 'RDN': 'rdn', 'REP': 'rep', 'REQ': 'req', 'RLC': 'rlc', 'SAN': 'santiment', 'SNGLS': 'sng', 'SNT': 'status', 'SPANK': 'spk', 'STORJ': 'stj', 'TNB': 'tnb', 'TRX': 'trx', 'USD': 'wire', 'USDC': 'udc', // https://github.com/ccxt/ccxt/issues/5833 'UTK': 'utk', 'USDT': 'tetheruso', // Tether on Omni // 'USDT': 'tetheruse', // Tether on ERC20 // 'USDT': 'tetherusl', // Tether on Liquid // 'USDT': 'tetherusx', // Tether on Tron // 'USDT': 'tetheruss', // Tether on EOS 'VEE': 'vee', 'WAX': 'wax', 'XLM': 'xlm', 'XMR': 'monero', 'XRP': 'ripple', 'XVG': 'xvg', 'YOYOW': 'yoyow', 'ZEC': 'zcash', 'ZRX': 'zrx', 'XTZ': 'xtz', }, 'orderTypes': { 'limit': 'exchange limit', 'market': 'exchange market', }, }, }); } async fetchFundingFees (params = {}) { await this.loadMarkets (); const response = await this.privatePostAccountFees (params); const fees = response['withdraw']; const withdraw = {}; const ids = Object.keys (fees); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const code = this.safeCurrencyCode (id); withdraw[code] = this.safeFloat (fees, id); } return { 'info': response, 'withdraw': withdraw, 'deposit': withdraw, // only for deposits of less than $1000 }; } async fetchTradingFees (params = {}) { await this.loadMarkets (); const response = await this.privatePostSummary (params); // // { // time: '2019-02-20T15:50:19.152000Z', // trade_vol_30d: [ // { // curr: 'Total (USD)', // vol: 0, // vol_maker: 0, // vol_BFX: 0, // vol_BFX_maker: 0, // vol_ETHFX: 0, // vol_ETHFX_maker: 0 // } // ], // fees_funding_30d: {}, // fees_funding_total_30d: 0, // fees_trading_30d: {}, // fees_trading_total_30d: 0, // maker_fee: 0.001, // taker_fee: 0.002 // } // return { 'info': response, 'maker': this.safeFloat (response, 'maker_fee'), 'taker': this.safeFloat (response, 'taker_fee'), }; } async fetchMarkets (params = {}) { const ids = await this.publicGetSymbols (); const details = await this.publicGetSymbolsDetails (); const result = []; for (let i = 0; i < details.length; i++) { const market = details[i]; let id = this.safeString (market, 'pair'); if (!this.inArray (id, ids)) { continue; } id = id.toUpperCase (); let baseId = undefined; let quoteId = undefined; if (id.indexOf (':') >= 0) { const parts = id.split (':'); baseId = parts[0]; quoteId = parts[1]; } else { baseId = id.slice (0, 3); quoteId = id.slice (3, 6); } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'price': market['price_precision'], 'amount': undefined, }; const limits = { 'amount': { 'min': this.safeFloat (market, 'minimum_order_size'), 'max': this.safeFloat (market, 'maximum_order_size'), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, }; limits['cost'] = { 'min': limits['amount']['min'] * limits['price']['min'], 'max': undefined, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } amountToPrecision (symbol, amount) { return this.numberToString (amount); } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; const rate = market[takerOrMaker]; let cost = amount * rate; let key = 'quote'; if (side === 'sell') { cost *= price; } else { key = 'base'; } const code = market[key]; const currency = this.safeValue (this.currencies, code); if (currency !== undefined) { const precision = this.safeInteger (currency, 'precision'); if (precision !== undefined) { cost = parseFloat (this.currencyToPrecision (code, cost)); } } return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': cost, }; } async fetchBalance (params = {}) { await this.loadMarkets (); const balanceType = this.safeString (params, 'type', 'exchange'); const query = this.omit (params, 'type'); const response = await this.privatePostBalances (query); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; if (balance['type'] === balanceType) { const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); // bitfinex had BCH previously, now it's BAB, but the old // BCH symbol is kept for backward-compatibility // we need a workaround here so that the old BCH balance // would not override the new BAB balance (BAB is unified to BCH) // https://github.com/ccxt/ccxt/issues/4989 if (!(code in result)) { const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['total'] = this.safeFloat (balance, 'amount'); result[code] = account; } } } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; if (limit !== undefined) { request['limit_bids'] = limit; request['limit_asks'] = limit; } const response = await this.publicGetBookSymbol (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickers (params); const result = {}; for (let i = 0; i < response.length; i++) { const ticker = this.parseTicker (response[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const ticker = await this.publicGetPubtickerSymbol (this.extend (request, params)); return this.parseTicker (ticker, market); } parseTicker (ticker, market = undefined) { let timestamp = this.safeFloat (ticker, 'timestamp'); if (timestamp !== undefined) { timestamp *= 1000; } timestamp = parseInt (timestamp); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } else if ('pair' in ticker) { const marketId = this.safeString (ticker, 'pair'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const baseId = marketId.slice (0, 3); const quoteId = marketId.slice (3, 6); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } } const last = this.safeFloat (ticker, 'last_price'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker, 'mid'), 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } parseTrade (trade, market) { const id = this.safeString (trade, 'tid'); let timestamp = this.safeFloat (trade, 'timestamp'); if (timestamp !== undefined) { timestamp = parseInt (timestamp) * 1000; } const type = undefined; const side = this.safeStringLower (trade, 'type'); const orderId = this.safeString (trade, 'order_id'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let fee = undefined; if ('fee_amount' in trade) { const feeCost = -this.safeFloat (trade, 'fee_amount'); const feeCurrencyId = this.safeString (trade, 'fee_currency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': market['symbol'], 'type': type, 'order': orderId, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'limit_trades': limit, }; if (since !== undefined) { request['timestamp'] = parseInt (since / 1000); } const response = await this.publicGetTradesSymbol (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit_trades'] = limit; } if (since !== undefined) { request['timestamp'] = parseInt (since / 1000); } const response = await this.privatePostMytrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), 'side': side, 'amount': this.amountToPrecision (symbol, amount), 'type': this.safeString (this.options['orderTypes'], type, type), 'ocoorder': false, 'buy_price_oco': 0, 'sell_price_oco': 0, }; if (type === 'market') { request['price'] = this.nonce ().toString (); } else { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostOrderNew (this.extend (request, params)); return this.parseOrder (response); } async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { await this.loadMarkets (); const order = { 'order_id': id, }; if (price !== undefined) { order['price'] = this.priceToPrecision (symbol, price); } if (amount !== undefined) { order['amount'] = this.numberToString (amount); } if (symbol !== undefined) { order['symbol'] = this.marketId (symbol); } if (side !== undefined) { order['side'] = side; } if (type !== undefined) { order['type'] = this.safeString (this.options['orderTypes'], type, type); } const response = await this.privatePostOrderCancelReplace (this.extend (order, params)); return this.parseOrder (response); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': parseInt (id), }; return await this.privatePostOrderCancel (this.extend (request, params)); } async cancelAllOrders (symbol = undefined, params = {}) { return await this.privatePostOrderCancelAll (params); } parseOrder (order, market = undefined) { const side = this.safeString (order, 'side'); const open = this.safeValue (order, 'is_live'); const canceled = this.safeValue (order, 'is_cancelled'); let status = undefined; if (open) { status = 'open'; } else if (canceled) { status = 'canceled'; } else { status = 'closed'; } let symbol = undefined; if (market === undefined) { const marketId = this.safeStringUpper (order, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } } if (market !== undefined) { symbol = market['symbol']; } let orderType = order['type']; const exchange = orderType.indexOf ('exchange ') >= 0; if (exchange) { const parts = order['type'].split (' '); orderType = parts[1]; } let timestamp = this.safeFloat (order, 'timestamp'); if (timestamp !== undefined) { timestamp = parseInt (timestamp) * 1000; } const id = this.safeString (order, 'id'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': orderType, 'side': side, 'price': this.safeFloat (order, 'price'), 'average': this.safeFloat (order, 'avg_execution_price'), 'amount': this.safeFloat (order, 'original_amount'), 'remaining': this.safeFloat (order, 'remaining_amount'), 'filled': this.safeFloat (order, 'executed_amount'), 'status': status, 'fee': undefined, 'cost': undefined, 'trades': undefined, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (symbol !== undefined) { if (!(symbol in this.markets)) { throw new ExchangeError (this.id + ' has no symbol ' + symbol); } } const response = await this.privatePostOrders (params); let orders = this.parseOrders (response, undefined, since, limit); if (symbol !== undefined) { orders = this.filterBy (orders, 'symbol', symbol); } return orders; } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privatePostOrdersHist (this.extend (request, params)); let orders = this.parseOrders (response, undefined, since, limit); if (symbol !== undefined) { orders = this.filterBy (orders, 'symbol', symbol); } orders = this.filterByArray (orders, 'status', [ 'closed', 'canceled' ], false); return orders; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': parseInt (id), }; const response = await this.privatePostOrderStatus (this.extend (request, params)); return this.parseOrder (response); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ this.safeInteger (ohlcv, 0), this.safeFloat (ohlcv, 1), this.safeFloat (ohlcv, 3), this.safeFloat (ohlcv, 4), this.safeFloat (ohlcv, 2), this.safeFloat (ohlcv, 5), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (limit === undefined) { limit = 100; } const market = this.market (symbol); const v2id = 't' + market['id']; const request = { 'symbol': v2id, 'timeframe': this.timeframes[timeframe], 'sort': 1, 'limit': limit, }; if (since !== undefined) { request['start'] = since; } const response = await this.v2GetCandlesTradeTimeframeSymbolHist (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } getCurrencyName (code) { // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method if (code in this.options['currencyNames']) { return this.options['currencyNames'][code]; } throw new NotSupported (this.id + ' ' + code + ' not supported for withdrawal'); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const request = { 'renew': 1, }; const response = await this.fetchDepositAddress (code, this.extend (request, params)); return response; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method const name = this.getCurrencyName (code); const request = { 'method': name, 'wallet_name': 'exchange', 'renew': 0, // a value of 1 will generate a new address }; const response = await this.privatePostDepositNew (this.extend (request, params)); let address = this.safeValue (response, 'address'); let tag = undefined; if ('address_pool' in response) { tag = address; address = response['address_pool']; } this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency `code` argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; if (since !== undefined) { request['since'] = parseInt (since / 1000); } const response = await this.privatePostHistoryMovements (this.extend (request, params)); // // [ // { // "id":581183, // "txid": 123456, // "currency":"BTC", // "method":"BITCOIN", // "type":"WITHDRAWAL", // "amount":".01", // "description":"3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ, offchain transfer ", // "address":"3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ", // "status":"COMPLETED", // "timestamp":"1443833327.0", // "timestamp_created": "1443833327.1", // "fee": 0.1, // } // ] // return this.parseTransactions (response, currency, since, limit); } parseTransaction (transaction, currency = undefined) { // // crypto // // { // "id": 12042490, // "fee": "-0.02", // "txid": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D", // "type": "DEPOSIT", // "amount": "2099.849999", // "method": "RIPPLE", // "status": "COMPLETED", // "address": "2505189261", // "currency": "XRP", // "timestamp": "1551730524.0", // "description": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D", // "timestamp_created": "1551730523.0" // } // // fiat // // { // "id": 12725095, // "fee": "-60.0", // "txid": null, // "type": "WITHDRAWAL", // "amount": "9943.0", // "method": "WIRE", // "status": "SENDING", // "address": null, // "currency": "EUR", // "timestamp": "1561802484.0", // "description": "Name: bob, AccountAddress: some address, Account: someaccountno, Bank: bank address, SWIFT: foo, Country: UK, Details of Payment: withdrawal name, Intermediary Bank Name: , Intermediary Bank Address: , Intermediary Bank City: , Intermediary Bank Country: , Intermediary Bank Account: , Intermediary Bank SWIFT: , Fee: -60.0", // "timestamp_created": "1561716066.0" // } // let timestamp = this.safeFloat (transaction, 'timestamp_created'); if (timestamp !== undefined) { timestamp = parseInt (timestamp * 1000); } let updated = this.safeFloat (transaction, 'timestamp'); if (updated !== undefined) { updated = parseInt (updated * 1000); } const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const type = this.safeStringLower (transaction, 'type'); // DEPOSIT or WITHDRAWAL const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); let feeCost = this.safeFloat (transaction, 'fee'); if (feeCost !== undefined) { feeCost = Math.abs (feeCost); } return { 'info': transaction, 'id': this.safeString (transaction, 'id'), 'txid': this.safeString (transaction, 'txid'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': this.safeString (transaction, 'address'), // todo: this is actually the tag for XRP transfers (the address is missing) 'tag': undefined, // refix it properly for the tag from description 'type': type, 'amount': this.safeFloat (transaction, 'amount'), 'currency': code, 'status': status, 'updated': updated, 'fee': { 'currency': code, 'cost': feeCost, 'rate': undefined, }, }; } parseTransactionStatus (status) { const statuses = { 'SENDING': 'pending', 'CANCELED': 'canceled', 'ZEROCONFIRMED': 'failed', // ZEROCONFIRMED happens e.g. in a double spend attempt (I had one in my movements!) 'COMPLETED': 'ok', }; return this.safeString (statuses, status, status); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method const name = this.getCurrencyName (code); const request = { 'withdraw_type': name, 'walletselected': 'exchange', 'amount': this.numberToString (amount), 'address': address, }; if (tag !== undefined) { request['payment_id'] = tag; } const responses = await this.privatePostWithdraw (this.extend (request, params)); const response = responses[0]; const id = this.safeString (response, 'withdrawal_id'); const message = this.safeString (response, 'message'); const errorMessage = this.findBroadlyMatchedKey (this.exceptions['broad'], message); if (id === 0) { if (errorMessage !== undefined) { const ExceptionClass = this.exceptions['broad'][errorMessage]; throw new ExceptionClass (this.id + ' ' + message); } throw new ExchangeError (this.id + ' withdraw returned an id of zero: ' + this.json (response)); } return { 'info': response, 'id': id, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/' + this.implodeParams (path, params); if (api === 'v2') { request = '/' + api + request; } else { request = '/' + this.version + request; } let query = this.omit (params, this.extractParams (path)); let url = this.urls['api'][api] + request; if ((api === 'public') || (path.indexOf ('/hist') >= 0)) { if (Object.keys (query).length) { const suffix = '?' + this.urlencode (query); url += suffix; request += suffix; } } if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce (); query = this.extend ({ 'nonce': nonce.toString (), 'request': request, }, query); body = this.json (query); query = this.encode (body); const payload = this.stringToBase64 (query); const secret = this.encode (this.secret); const signature = this.hmac (payload, secret, 'sha384'); headers = { 'X-BFX-APIKEY': this.apiKey, 'X-BFX-PAYLOAD': this.decode (payload), 'X-BFX-SIGNATURE': signature, }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } if (code >= 400) { if (body[0] === '{') { const feedback = this.id + ' ' + body; const message = this.safeString2 (response, 'message', 'error'); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],32:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const bitfinex = require ('./bitfinex.js'); const { ExchangeError, InvalidAddress, ArgumentsRequired, InsufficientFunds, AuthenticationError, OrderNotFound, InvalidOrder, BadRequest, InvalidNonce, BadSymbol, OnMaintenance } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitfinex2 extends bitfinex { describe () { return this.deepExtend (super.describe (), { 'id': 'bitfinex2', 'name': 'Bitfinex', 'countries': [ 'VG' ], 'version': 'v2', 'certified': false, 'pro': false, // new metainfo interface 'has': { 'CORS': false, 'cancelAllOrders': true, 'createDepositAddress': true, 'createLimitOrder': true, 'createMarketOrder': true, 'createOrder': true, 'cancelOrder': true, 'deposit': false, 'editOrder': false, 'fetchDepositAddress': true, 'fetchClosedOrders': false, 'fetchFundingFees': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': false, 'fetchOpenOrder': true, 'fetchClosedOrder': true, 'fetchOrderTrades': true, 'fetchStatus': true, 'fetchTickers': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTransactions': false, 'withdraw': true, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '3h': '3h', '6h': '6h', '12h': '12h', '1d': '1D', '1w': '7D', '2w': '14D', '1M': '1M', }, 'rateLimit': 1500, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg', 'api': { 'v1': 'https://api.bitfinex.com', 'public': 'https://api-pub.bitfinex.com', 'private': 'https://api.bitfinex.com', }, 'www': 'https://www.bitfinex.com', 'doc': [ 'https://docs.bitfinex.com/v2/docs/', 'https://github.com/bitfinexcom/bitfinex-api-node', ], 'fees': 'https://www.bitfinex.com/fees', }, 'api': { 'v1': { 'get': [ 'symbols', 'symbols_details', ], }, 'public': { 'get': [ 'conf/{config}', 'conf/pub:{action}:{object}', 'conf/pub:{action}:{object}:{detail}', 'conf/pub:map:{object}', 'conf/pub:map:{object}:{detail}', 'conf/pub:map:currency:{detail}', 'conf/pub:map:currency:sym', // maps symbols to their API symbols, BAB > BCH 'conf/pub:map:currency:label', // verbose friendly names, BNT > Bancor 'conf/pub:map:currency:unit', // maps symbols to unit of measure where applicable 'conf/pub:map:currency:undl', // maps derivatives symbols to their underlying currency 'conf/pub:map:currency:pool', // maps symbols to underlying network/protocol they operate on 'conf/pub:map:currency:explorer', // maps symbols to their recognised block explorer URLs 'conf/pub:map:tx:method', 'conf/pub:list:{object}', 'conf/pub:list:{object}:{detail}', 'conf/pub:list:currency', 'conf/pub:list:pair:exchange', 'conf/pub:list:pair:margin', 'conf/pub:list:competitions', 'conf/pub:info:{object}', 'conf/pub:info:{object}:{detail}', 'conf/pub:info:pair', 'conf/pub:info:tx:status', // [ deposit, withdrawal ] statuses 1 = active, 0 = maintenance 'conf/pub:fees', 'platform/status', 'tickers', 'ticker/{symbol}', 'trades/{symbol}/hist', 'book/{symbol}/{precision}', 'book/{symbol}/P0', 'book/{symbol}/P1', 'book/{symbol}/P2', 'book/{symbol}/P3', 'book/{symbol}/R0', 'stats1/{key}:{size}:{symbol}:{side}/{section}', 'stats1/{key}:{size}:{symbol}:{side}/last', 'stats1/{key}:{size}:{symbol}:{side}/hist', 'stats1/{key}:{size}:{symbol}/{section}', 'stats1/{key}:{size}:{symbol}/last', 'stats1/{key}:{size}:{symbol}/hist', 'stats1/{key}:{size}:{symbol}:long/last', 'stats1/{key}:{size}:{symbol}:long/hist', 'stats1/{key}:{size}:{symbol}:short/last', 'stats1/{key}:{size}:{symbol}:short/hist', 'candles/trade:{timeframe}:{symbol}/{section}', 'candles/trade:{timeframe}:{symbol}/last', 'candles/trade:{timeframe}:{symbol}/hist', 'status/{type}', 'status/deriv', 'liquidations/hist', 'rankings/{key}:{timeframe}:{symbol}/{section}', 'rankings/{key}:{timeframe}:{symbol}/hist', ], 'post': [ 'calc/trade/avg', 'calc/fx', ], }, 'private': { 'post': [ // 'auth/r/orders/{symbol}/new', // outdated // 'auth/r/stats/perf:{timeframe}/hist', // outdated 'auth/r/wallets', 'auth/r/wallets/hist', 'auth/r/orders', 'auth/r/orders/{symbol}', 'auth/w/order/submit', 'auth/w/order/update', 'auth/w/order/cancel', 'auth/w/order/multi', 'auth/w/order/cancel/multi', 'auth/r/orders/{symbol}/hist', 'auth/r/orders/hist', 'auth/r/order/{symbol}:{id}/trades', 'auth/r/trades/{symbol}/hist', 'auth/r/trades/hist', 'auth/r/ledgers/{currency}/hist', 'auth/r/ledgers/hist', 'auth/r/info/margin/{key}', 'auth/r/info/margin/base', 'auth/r/info/margin/sym_all', 'auth/r/positions', 'auth/w/position/claim', 'auth/r/positions/hist', 'auth/r/positions/audit', 'auth/w/deriv/collateral/set', 'auth/r/funding/offers', 'auth/r/funding/offers/{symbol}', 'auth/w/funding/offer/submit', 'auth/w/funding/offer/cancel', 'auth/w/funding/offer/cancel/all', 'auth/w/funding/close', 'auth/w/funding/auto', 'auth/w/funding/keep', 'auth/r/funding/offers/{symbol}/hist', 'auth/r/funding/offers/hist', 'auth/r/funding/loans', 'auth/r/funding/loans/hist', 'auth/r/funding/loans/{symbol}', 'auth/r/funding/loans/{symbol}/hist', 'auth/r/funding/credits', 'auth/r/funding/credits/hist', 'auth/r/funding/credits/{symbol}', 'auth/r/funding/credits/{symbol}/hist', 'auth/r/funding/trades/{symbol}/hist', 'auth/r/funding/trades/hist', 'auth/r/info/funding/{key}', 'auth/r/info/user', 'auth/r/logins/hist', 'auth/w/transfer', 'auth/w/deposit/address', 'auth/w/deposit/invoice', 'auth/w/withdraw', 'auth/r/movements/{currency}/hist', 'auth/r/movements/hist', 'auth/r/alerts', 'auth/w/alert/set', 'auth/w/alert/price:{symbol}:{price}/del', 'auth/w/alert/{type}:{symbol}:{price}/del', 'auth/calc/order/avail', 'auth/w/settings/set', 'auth/r/settings', 'auth/w/settings/del', ], }, }, 'fees': { 'trading': { 'maker': 0.1 / 100, 'taker': 0.2 / 100, }, 'funding': { 'withdraw': { 'BTC': 0.0004, 'BCH': 0.0001, 'ETH': 0.00135, 'EOS': 0.0, 'LTC': 0.001, 'OMG': 0.15097, 'IOT': 0.0, 'NEO': 0.0, 'ETC': 0.01, 'XRP': 0.02, 'ETP': 0.01, 'ZEC': 0.001, 'BTG': 0.0, 'DASH': 0.01, 'XMR': 0.0001, 'QTM': 0.01, 'EDO': 0.23687, 'DAT': 9.8858, 'AVT': 1.1251, 'SAN': 0.35977, 'USDT': 5.0, 'SPK': 16.971, 'BAT': 1.1209, 'GNT': 2.8789, 'SNT': 9.0848, 'QASH': 1.726, 'YYW': 7.9464, }, }, }, 'options': { 'precision': 'R0', // P0, P1, P2, P3, P4, R0 // convert 'EXCHANGE MARKET' to lowercase 'market' // convert 'EXCHANGE LIMIT' to lowercase 'limit' // everything else remains uppercase 'exchangeTypes': { // 'MARKET': undefined, 'EXCHANGE MARKET': 'market', // 'LIMIT': undefined, 'EXCHANGE LIMIT': 'limit', // 'STOP': undefined, // 'EXCHANGE STOP': undefined, // 'TRAILING STOP': undefined, // 'EXCHANGE TRAILING STOP': undefined, // 'FOK': undefined, // 'EXCHANGE FOK': undefined, // 'STOP LIMIT': undefined, // 'EXCHANGE STOP LIMIT': undefined, // 'IOC': undefined, // 'EXCHANGE IOC': undefined, }, // convert 'market' to 'EXCHANGE MARKET' // convert 'limit' 'EXCHANGE LIMIT' // everything else remains as is 'orderTypes': { 'market': 'EXCHANGE MARKET', 'limit': 'EXCHANGE LIMIT', }, 'fiat': { 'USD': 'USD', 'EUR': 'EUR', 'JPY': 'JPY', 'GBP': 'GBP', }, }, 'exceptions': { 'exact': { '10020': BadRequest, '10100': AuthenticationError, '10114': InvalidNonce, '20060': OnMaintenance, }, 'broad': { 'address': InvalidAddress, 'available balance is only': InsufficientFunds, 'not enough exchange balance': InsufficientFunds, 'Order not found': OrderNotFound, 'symbol: invalid': BadSymbol, 'Invalid order': InvalidOrder, }, }, }); } isFiat (code) { return (code in this.options['fiat']); } getCurrencyId (code) { return 'f' + code; } async fetchStatus (params = {}) { // // [1] // operative // [0] // maintenance // const response = await this.publicGetPlatformStatus (params); const status = this.safeValue (response, 0); const formattedStatus = (status === 1) ? 'ok' : 'maintenance'; this.status = this.extend (this.status, { 'status': formattedStatus, 'updated': this.milliseconds (), }); return this.status; } async fetchMarkets (params = {}) { const response = await this.v1GetSymbolsDetails (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; let id = this.safeStringUpper (market, 'pair'); let baseId = undefined; let quoteId = undefined; if (id.indexOf (':') >= 0) { const parts = id.split (':'); baseId = parts[0]; quoteId = parts[1]; } else { baseId = id.slice (0, 3); quoteId = id.slice (3, 6); } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; id = 't' + id; baseId = this.getCurrencyId (baseId); quoteId = this.getCurrencyId (quoteId); const precision = { 'price': this.safeInteger (market, 'price_precision'), 'amount': this.safeInteger (market, 'price_precision'), }; const limits = { 'amount': { 'min': this.safeFloat (market, 'minimum_order_size'), 'max': this.safeFloat (market, 'maximum_order_size'), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, }; limits['cost'] = { 'min': limits['amount']['min'] * limits['price']['min'], 'max': undefined, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': limits, 'info': market, 'swap': false, 'spot': false, 'futures': false, }); } return result; } async fetchBalance (params = {}) { // this api call does not return the 'used' amount - use the v1 version instead (which also returns zero balances) await this.loadMarkets (); const response = await this.privatePostAuthRWallets (params); const balanceType = this.safeString (params, 'type', 'exchange'); const result = { 'info': response }; for (let b = 0; b < response.length; b++) { const balance = response[b]; const accountType = balance[0]; let currency = balance[1]; const total = balance[2]; const available = balance[4]; if (accountType === balanceType) { if (currency[0] === 't') { currency = currency.slice (1); } const code = this.safeCurrencyCode (currency); const account = this.account (); // do not fill in zeroes and missing values in the parser // rewrite and unify the following to use the unified parseBalance account['total'] = total; if (!available) { if (available === 0) { account['free'] = 0; account['used'] = total; } else { account['free'] = total; } } else { account['free'] = available; account['used'] = account['total'] - account['free']; } result[code] = account; } } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const precision = this.safeValue (this.options, 'precision', 'R0'); const request = { 'symbol': this.marketId (symbol), 'precision': precision, }; if (limit !== undefined) { request['len'] = limit; // 25 or 100 } const fullRequest = this.extend (request, params); const orderbook = await this.publicGetBookSymbolPrecision (fullRequest); const timestamp = this.milliseconds (); const result = { 'bids': [], 'asks': [], 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'nonce': undefined, }; const priceIndex = (fullRequest['precision'] === 'R0') ? 1 : 0; for (let i = 0; i < orderbook.length; i++) { const order = orderbook[i]; const price = order[priceIndex]; const amount = Math.abs (order[2]); const side = (order[2] > 0) ? 'bids' : 'asks'; result[side].push ([ price, amount ]); } result['bids'] = this.sortBy (result['bids'], 0, true); result['asks'] = this.sortBy (result['asks'], 0); return result; } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const length = ticker.length; const last = ticker[length - 4]; return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': ticker[length - 2], 'low': ticker[length - 1], 'bid': ticker[length - 10], 'bidVolume': undefined, 'ask': ticker[length - 8], 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': ticker[length - 6], 'percentage': ticker[length - 5] * 100, 'average': undefined, 'baseVolume': ticker[length - 3], 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbols !== undefined) { const ids = this.marketIds (symbols); request['symbols'] = ids.join (','); } else { request['symbols'] = 'ALL'; } const tickers = await this.publicGetTickers (this.extend (request, params)); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = tickers[i]; const id = ticker[0]; if (id in this.markets_by_id) { const market = this.markets_by_id[id]; const symbol = market['symbol']; result[symbol] = this.parseTicker (ticker, market); } } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const ticker = await this.publicGetTickerSymbol (this.extend (request, params)); return this.parseTicker (ticker, market); } parseSymbol (marketId) { if (marketId === undefined) { return marketId; } marketId = marketId.replace ('t', ''); let baseId = undefined; let quoteId = undefined; if (marketId.indexOf (':') >= 0) { const parts = marketId.split (':'); baseId = parts[0]; quoteId = parts[1]; } else { baseId = marketId.slice (0, 3); quoteId = marketId.slice (3, 6); } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); return base + '/' + quote; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // [ // ID, // MTS, // timestamp // AMOUNT, // PRICE // ] // // fetchMyTrades (private) // // [ // ID, // PAIR, // MTS_CREATE, // ORDER_ID, // EXEC_AMOUNT, // EXEC_PRICE, // ORDER_TYPE, // ORDER_PRICE, // MAKER, // FEE, // FEE_CURRENCY, // ... // ] // const tradeLength = trade.length; const isPrivate = (tradeLength > 5); const id = trade[0].toString (); const amountIndex = isPrivate ? 4 : 2; let amount = trade[amountIndex]; let cost = undefined; const priceIndex = isPrivate ? 5 : 3; const price = trade[priceIndex]; let side = undefined; let orderId = undefined; let takerOrMaker = undefined; let type = undefined; let fee = undefined; let symbol = undefined; const timestampIndex = isPrivate ? 2 : 1; const timestamp = trade[timestampIndex]; if (isPrivate) { const marketId = trade[1]; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = this.parseSymbol (marketId); } orderId = trade[3].toString (); takerOrMaker = (trade[8] === 1) ? 'maker' : 'taker'; let feeCost = trade[9]; const feeCurrency = this.safeCurrencyCode (trade[10]); if (feeCost !== undefined) { feeCost = -feeCost; if (symbol in this.markets) { feeCost = this.feeToPrecision (symbol, feeCost); } else { const currencyId = 'f' + feeCurrency; if (currencyId in this.currencies_by_id) { const currency = this.currencies_by_id[currencyId]; feeCost = this.currencyToPrecision (currency['code'], feeCost); } } fee = { 'cost': parseFloat (feeCost), 'currency': feeCurrency, }; } const orderType = trade[6]; type = this.safeString (this.options['exchangeTypes'], orderType); } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } if (amount !== undefined) { side = (amount < 0) ? 'sell' : 'buy'; amount = Math.abs (amount); if (cost === undefined) { if (price !== undefined) { cost = amount * price; } } } return { 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'side': side, 'type': type, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let sort = '-1'; const request = { 'symbol': market['id'], }; if (since !== undefined) { request['start'] = since; sort = '1'; } if (limit !== undefined) { request['limit'] = limit; // default 120, max 5000 } request['sort'] = sort; const response = await this.publicGetTradesSymbolHist (this.extend (request, params)); // // [ // [ // ID, // MTS, // timestamp // AMOUNT, // PRICE // ] // ] // const trades = this.sortBy (response, 1); return this.parseTrades (trades, market, undefined, limit); } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 100, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 100; // default 100, max 5000 } if (since === undefined) { since = this.milliseconds () - this.parseTimeframe (timeframe) * limit * 1000; } const request = { 'symbol': market['id'], 'timeframe': this.timeframes[timeframe], 'sort': 1, 'start': since, 'limit': limit, }; const response = await this.publicGetCandlesTradeTimeframeSymbolHist (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } parseOrderStatus (status) { const statuses = { 'ACTIVE': 'open', 'PARTIALLY FILLED': 'open', 'EXECUTED': 'closed', 'CANCELED': 'canceled', 'CANCELED was: PARTIALLY FILLED': 'canceled', 'INSUFFICIENT MARGIN': 'canceled', 'INSUFFICIENT BALANCE (G1) was: PARTIALLY FILLED': 'canceled', 'RSN_DUST': 'rejected', 'RSN_PAUSE': 'rejected', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const id = this.safeString (order, 0); let symbol = undefined; const marketId = this.safeString (order, 3); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { symbol = this.parseSymbol (marketId); } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } // https://github.com/ccxt/ccxt/issues/6686 // const timestamp = this.safeTimestamp (order, 5); const timestamp = this.safeInteger (order, 5); const remaining = Math.abs (this.safeFloat (order, 6)); const amount = Math.abs (this.safeFloat (order, 7)); const filled = amount - remaining; const side = (order[7] < 0) ? 'sell' : 'buy'; const orderType = this.safeString (order, 8); const type = this.safeString (this.safeValue (this.options, 'exchangeTypes'), orderType); let status = undefined; const statusString = this.safeString (order, 13); if (statusString !== undefined) { const parts = statusString.split (' @ '); status = this.parseOrderStatus (this.safeString (parts, 0)); } const price = this.safeFloat (order, 16); const average = this.safeFloat (order, 17); const cost = price * filled; const clientOrderId = this.safeString (order, 2); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const orderTypes = this.safeValue (this.options, 'orderTypes', {}); const orderType = this.safeString (orderTypes, type, type); amount = (side === 'sell') ? -amount : amount; const request = { 'symbol': market['id'], 'type': orderType, 'amount': this.numberToString (amount), }; if (type !== 'market') { request['price'] = this.numberToString (price); } const response = await this.privatePostAuthWOrderSubmit (this.extend (request, params)); // // [ // 1578784364.748, // Millisecond Time Stamp of the update // "on-req", // Purpose of notification ('on-req', 'oc-req', 'uca', 'fon-req', 'foc-req') // null, // Unique ID of the message // null, // Ignore // [ // [ // 37271830598, // Order ID // null, // Group ID // 1578784364748, // Client Order ID // "tBTCUST", // Pair // 1578784364748, // Millisecond timestamp of creation // 1578784364748, // Millisecond timestamp of update // -0.005, // Positive means buy, negative means sell // -0.005, // Original amount // "EXCHANGE LIMIT", // Order type (LIMIT, MARKET, STOP, TRAILING STOP, EXCHANGE MARKET, EXCHANGE LIMIT, EXCHANGE STOP, EXCHANGE TRAILING STOP, FOK, EXCHANGE FOK, IOC, EXCHANGE IOC) // null, // Previous order type // null, // Millisecond timestamp of Time-In-Force: automatic order cancellation // null, // Ignore // 0, // Flags (see https://docs.bitfinex.com/docs/flag-values) // "ACTIVE", // Order Status // null, // Ignore // null, // Ignore // 20000, // Price // 0, // Average price // 0, // The trailing price // 0, // Auxiliary Limit price (for STOP LIMIT) // null, // Ignore // null, // Ignore // null, // Ignore // 0, // 1 - hidden order // null, // If another order caused this order to be placed (OCO) this will be that other order's ID // null, // Ignore // null, // Ignore // null, // Ignore // "API>BFX", // Origin of action: BFX, ETHFX, API>BFX, API>ETHFX // null, // Ignore // null, // Ignore // null // Meta // ] // ], // null, // Error code // "SUCCESS", // Status (SUCCESS, ERROR, FAILURE, ...) // "Submitting 1 orders." // Text of the notification // ] // const status = this.safeString (response, 6); if (status !== 'SUCCESS') { const errorCode = response[5]; const errorText = response[7]; throw new ExchangeError (this.id + ' ' + response[6] + ': ' + errorText + ' (#' + errorCode + ')'); } const orders = this.safeValue (response, 4, []); const order = this.safeValue (orders, 0); return this.parseOrder (order, market); } async cancelAllOrders (symbol = undefined, params = {}) { const request = { 'all': 1, }; const response = await this.privatePostAuthWOrderCancelMulti (this.extend (request, params)); const orders = this.safeValue (response, 4, []); return this.parseOrders (orders); } async cancelOrder (id, symbol = undefined, params = {}) { const cid = this.safeValue (params, 'cid'); // client order id let request = undefined; if (cid !== undefined) { const cidDate = this.safeValue (params, 'cidDate'); // client order id date if (cidDate === undefined) { throw new InvalidOrder (this.id + " canceling an order by client order id ('cid') requires both 'cid' and 'cid_date' ('YYYY-MM-DD')"); } request = { 'cid': cid, 'cid_date': cidDate, }; } else { request = { 'id': parseInt (id), }; } const response = await this.privatePostAuthWOrderCancel (this.extend (request, params)); const order = this.safeValue (response, 4); return this.parseOrder (order); } async fetchOpenOrder (id, symbol = undefined, params = {}) { const request = { 'id': [ parseInt (id) ], }; const orders = await this.fetchOpenOrders (symbol, undefined, undefined, this.extend (request, params)); const order = this.safeValue (orders, 0); if (order === undefined) { throw new OrderNotFound (this.id + ' order ' + id + ' not found'); } return order; } async fetchClosedOrder (id, symbol = undefined, params = {}) { const request = { 'id': [ parseInt (id) ], }; const orders = await this.fetchClosedOrders (symbol, undefined, undefined, this.extend (request, params)); const order = this.safeValue (orders, 0); if (order === undefined) { throw new OrderNotFound (this.id + ' order ' + id + ' not found'); } return order; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; let response = undefined; if (symbol === undefined) { response = await this.privatePostAuthROrders (this.extend (request, params)); } else { market = this.market (symbol); request['symbol'] = market['id']; response = await this.privatePostAuthROrdersSymbol (this.extend (request, params)); } return this.parseOrders (response, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { // returns the most recent closed or canceled orders up to circa two weeks ago await this.loadMarkets (); const request = {}; let market = undefined; let response = undefined; if (symbol === undefined) { response = await this.privatePostAuthROrdersHist (this.extend (request, params)); } else { market = this.market (symbol); request['symbol'] = market['id']; response = await this.privatePostAuthROrdersSymbolHist (this.extend (request, params)); } if (since !== undefined) { request['start'] = since; } if (limit !== undefined) { request['limit'] = limit; // default 25, max 2500 } return this.parseOrders (response, market, since, limit); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrderTrades() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const orderId = parseInt (id); const request = { 'id': orderId, 'symbol': market['id'], }; // valid for trades upto 10 days old const response = await this.privatePostAuthROrderSymbolIdTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = { 'end': this.milliseconds (), }; if (since !== undefined) { request['start'] = since; } if (limit !== undefined) { request['limit'] = limit; // default 25, max 1000 } let method = 'privatePostAuthRTradesHist'; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; method = 'privatePostAuthRTradesSymbolHist'; } const response = await this[method] (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const request = { 'op_renew': 1, }; const response = await this.fetchDepositAddress (code, this.extend (request, params)); return response; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method const name = this.getCurrencyName (code); const request = { 'method': name, 'wallet': 'exchange', // 'exchange', 'margin', 'funding' and also old labels 'exchange', 'trading', 'deposit', respectively 'op_renew': 0, // a value of 1 will generate a new address }; const response = await this.privatePostAuthWDepositAddress (this.extend (request, params)); // // [ // 1582269616687, // MTS Millisecond Time Stamp of the update // 'acc_dep', // TYPE Purpose of notification 'acc_dep' for account deposit // null, // MESSAGE_ID unique ID of the message // null, // not documented // [ // null, // PLACEHOLDER // 'BITCOIN', // METHOD Method of deposit // 'BTC', // CURRENCY_CODE Currency code of new address // null, // PLACEHOLDER // '1BC9PZqpUmjyEB54uggn8TFKj49zSDYzqG', // ADDRESS // null, // POOL_ADDRESS // ], // null, // CODE null or integer work in progress // 'SUCCESS', // STATUS Status of the notification, SUCCESS, ERROR, FAILURE // 'success', // TEXT Text of the notification // ] // const result = this.safeValue (response, 4, []); const poolAddress = this.safeString (result, 5); const address = (poolAddress === undefined) ? this.safeString (result, 4) : poolAddress; const tag = (poolAddress === undefined) ? undefined : this.safeString (result, 4); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } parseTransaction (transaction, currency = undefined) { // // withdraw // // [ // 1582271520931, // MTS Millisecond Time Stamp of the update // "acc_wd-req", // TYPE Purpose of notification 'acc_wd-req' account withdrawal request // null, // MESSAGE_ID unique ID of the message // null, // not documented // [ // 0, // WITHDRAWAL_ID Unique Withdrawal ID // null, // PLACEHOLDER // "bitcoin", // METHOD Method of withdrawal // null, // PAYMENT_ID Payment ID if relevant // "exchange", // WALLET Sending wallet // 1, // AMOUNT Amount of Withdrawal less fee // null, // PLACEHOLDER // null, // PLACEHOLDER // 0.0004, // WITHDRAWAL_FEE Fee on withdrawal // ], // null, // CODE null or integer Work in progress // "SUCCESS", // STATUS Status of the notification, it may vary over time SUCCESS, ERROR, FAILURE // "Invalid bitcoin address (abcdef)", // TEXT Text of the notification // ] // // todo add support for all movements, deposits and withdrawals // const data = this.safeValue (transaction, 4, []); const timestamp = this.safeInteger (transaction, 0); let code = undefined; if (currency !== undefined) { code = currency['code']; } let feeCost = this.safeFloat (data, 8); if (feeCost !== undefined) { feeCost = Math.abs (feeCost); } const amount = this.safeFloat (data, 5); let id = this.safeValue (data, 0); let status = 'ok'; if (id === 0) { id = undefined; status = 'failed'; } return { 'info': transaction, 'id': id, 'txid': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': undefined, // this is actually the tag for XRP transfers (the address is missing) 'tag': this.safeString (data, 3), // refix it properly for the tag from description 'type': 'withdrawal', 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': { 'currency': code, 'cost': feeCost, 'rate': undefined, }, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); // todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method const name = this.getCurrencyName (code); const request = { 'method': name, 'wallet': 'exchange', // 'exchange', 'margin', 'funding' and also old labels 'exchange', 'trading', 'deposit', respectively 'amount': this.numberToString (amount), 'address': address, }; if (tag !== undefined) { request['payment_id'] = tag; } const response = await this.privatePostAuthWWithdraw (this.extend (request, params)); // // [ // 1582271520931, // MTS Millisecond Time Stamp of the update // "acc_wd-req", // TYPE Purpose of notification 'acc_wd-req' account withdrawal request // null, // MESSAGE_ID unique ID of the message // null, // not documented // [ // 0, // WITHDRAWAL_ID Unique Withdrawal ID // null, // PLACEHOLDER // "bitcoin", // METHOD Method of withdrawal // null, // PAYMENT_ID Payment ID if relevant // "exchange", // WALLET Sending wallet // 1, // AMOUNT Amount of Withdrawal less fee // null, // PLACEHOLDER // null, // PLACEHOLDER // 0.0004, // WITHDRAWAL_FEE Fee on withdrawal // ], // null, // CODE null or integer Work in progress // "SUCCESS", // STATUS Status of the notification, it may vary over time SUCCESS, ERROR, FAILURE // "Invalid bitcoin address (abcdef)", // TEXT Text of the notification // ] // const text = this.safeString (response, 7); if (text !== 'success') { this.throwBroadlyMatchedException (this.exceptions['broad'], text, text); } const transaction = this.parseTransaction (response, currency); return this.extend (transaction, { 'address': address, }); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'v1') { request = api + request; } else { request = this.version + request; } let url = this.urls['api'][api] + '/' + request; if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); body = this.json (query); const auth = '/api/' + request + nonce + body; const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha384'); headers = { 'bfx-nonce': nonce, 'bfx-apikey': this.apiKey, 'bfx-signature': signature, 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if (response) { if ('message' in response) { if (response['message'].indexOf ('not enough exchange balance') >= 0) { throw new InsufficientFunds (this.id + ' ' + this.json (response)); } throw new ExchangeError (this.id + ' ' + this.json (response)); } return response; } else if (response === '') { throw new ExchangeError (this.id + ' returned empty response'); } return response; } handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { if (statusCode === 500) { // See https://docs.bitfinex.com/docs/abbreviations-glossary#section-errorinfo-codes const errorCode = this.numberToString (response[1]); const errorText = response[2]; const feedback = this.id + ' ' + errorText; this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], errorText, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], errorText, feedback); throw new ExchangeError (this.id + ' ' + errorText + ' (#' + errorCode + ')'); } } }; },{"./base/errors":9,"./bitfinex.js":31}],33:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, OrderNotFound } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitflyer extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitflyer', 'name': 'bitFlyer', 'countries': [ 'JP' ], 'version': 'v1', 'rateLimit': 1000, // their nonce-timestamp is in seconds... 'has': { 'CORS': false, 'withdraw': true, 'fetchMyTrades': true, 'fetchOrders': true, 'fetchOrder': 'emulated', 'fetchOpenOrders': 'emulated', 'fetchClosedOrders': 'emulated', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/28051642-56154182-660e-11e7-9b0d-6042d1e6edd8.jpg', 'api': 'https://api.bitflyer.jp', 'www': 'https://bitflyer.jp', 'doc': 'https://lightning.bitflyer.com/docs?lang=en', }, 'api': { 'public': { 'get': [ 'getmarkets/usa', // new (wip) 'getmarkets/eu', // new (wip) 'getmarkets', // or 'markets' 'getboard', // ... 'getticker', 'getexecutions', 'gethealth', 'getboardstate', 'getchats', ], }, 'private': { 'get': [ 'getpermissions', 'getbalance', 'getbalancehistory', 'getcollateral', 'getcollateralhistory', 'getcollateralaccounts', 'getaddresses', 'getcoinins', 'getcoinouts', 'getbankaccounts', 'getdeposits', 'getwithdrawals', 'getchildorders', 'getparentorders', 'getparentorder', 'getexecutions', 'getpositions', 'gettradingcommission', ], 'post': [ 'sendcoin', 'withdraw', 'sendchildorder', 'cancelchildorder', 'sendparentorder', 'cancelparentorder', 'cancelallchildorders', ], }, }, 'fees': { 'trading': { 'maker': 0.2 / 100, 'taker': 0.2 / 100, }, 'BTC/JPY': { 'maker': 0.15 / 100, 'taker': 0.15 / 100, }, }, }); } async fetchMarkets (params = {}) { const jp_markets = await this.publicGetGetmarkets (params); const us_markets = await this.publicGetGetmarketsUsa (params); const eu_markets = await this.publicGetGetmarketsEu (params); let markets = this.arrayConcat (jp_markets, us_markets); markets = this.arrayConcat (markets, eu_markets); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'product_code'); const currencies = id.split ('_'); let baseId = undefined; let quoteId = undefined; let base = undefined; let quote = undefined; const numCurrencies = currencies.length; if (numCurrencies === 1) { baseId = id.slice (0, 3); quoteId = id.slice (3, 6); } else if (numCurrencies === 2) { baseId = currencies[0]; quoteId = currencies[1]; } else { baseId = currencies[1]; quoteId = currencies[2]; } base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); const symbol = (numCurrencies === 2) ? (base + '/' + quote) : id; const fees = this.safeValue (this.fees, symbol, this.fees['trading']); let maker = this.safeValue (fees, 'maker', this.fees['trading']['maker']); let taker = this.safeValue (fees, 'taker', this.fees['trading']['taker']); let spot = true; let future = false; let type = 'spot'; if (('alias' in market) || (currencies[0] === 'FX')) { type = 'future'; future = true; spot = false; maker = 0.0; taker = 0.0; } result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'maker': maker, 'taker': taker, 'type': type, 'spot': spot, 'future': future, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetGetbalance (params); // // [ // { // "currency_code": "JPY", // "amount": 1024078, // "available": 508000 // }, // { // "currency_code": "BTC", // "amount": 10.24, // "available": 4.12 // }, // { // "currency_code": "ETH", // "amount": 20.48, // "available": 16.38 // } // ] // const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency_code'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balance, 'amount'); account['free'] = this.safeFloat (balance, 'available'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'product_code': this.marketId (symbol), }; const orderbook = await this.publicGetGetboard (this.extend (request, params)); return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'size'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'product_code': this.marketId (symbol), }; const ticker = await this.publicGetGetticker (this.extend (request, params)); const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); const last = this.safeFloat (ticker, 'ltp'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'best_bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'best_ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume_by_product'), 'quoteVolume': undefined, 'info': ticker, }; } parseTrade (trade, market = undefined) { let side = this.safeStringLower (trade, 'side'); if (side !== undefined) { if (side.length < 1) { side = undefined; } } let order = undefined; if (side !== undefined) { const id = side + '_child_order_acceptance_id'; if (id in trade) { order = trade[id]; } } if (order === undefined) { order = this.safeString (trade, 'child_order_acceptance_id'); } const timestamp = this.parse8601 (this.safeString (trade, 'exec_date')); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'size'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } const id = this.safeString (trade, 'id'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': order, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'product_code': market['id'], }; const response = await this.publicGetGetexecutions (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'product_code': this.marketId (symbol), 'child_order_type': type.toUpperCase (), 'side': side.toUpperCase (), 'price': price, 'size': amount, }; const result = await this.privatePostSendchildorder (this.extend (request, params)); // { "status": - 200, "error_message": "Insufficient funds", "data": null } const id = this.safeString (result, 'child_order_acceptance_id'); return { 'info': result, 'id': id, }; } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder() requires a `symbol` argument'); } await this.loadMarkets (); const request = { 'product_code': this.marketId (symbol), 'child_order_acceptance_id': id, }; return await this.privatePostCancelchildorder (this.extend (request, params)); } parseOrderStatus (status) { const statuses = { 'ACTIVE': 'open', 'COMPLETED': 'closed', 'CANCELED': 'canceled', 'EXPIRED': 'canceled', 'REJECTED': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const timestamp = this.parse8601 (this.safeString (order, 'child_order_date')); const amount = this.safeFloat (order, 'size'); const remaining = this.safeFloat (order, 'outstanding_size'); const filled = this.safeFloat (order, 'executed_size'); const price = this.safeFloat (order, 'price'); const cost = price * filled; const status = this.parseOrderStatus (this.safeString (order, 'child_order_state')); const type = this.safeStringLower (order, 'child_order_type'); const side = this.safeStringLower (order, 'side'); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'product_code'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } let fee = undefined; const feeCost = this.safeFloat (order, 'total_commission'); if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': undefined, 'rate': undefined, }; } const id = this.safeString (order, 'child_order_acceptance_id'); return { 'id': id, 'clientOrderId': undefined, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'fee': fee, 'average': undefined, 'trades': undefined, }; } async fetchOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders() requires a `symbol` argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'product_code': market['id'], 'count': limit, }; const response = await this.privateGetGetchildorders (this.extend (request, params)); let orders = this.parseOrders (response, market, since, limit); if (symbol !== undefined) { orders = this.filterBy (orders, 'symbol', symbol); } return orders; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { const request = { 'child_order_state': 'ACTIVE', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { const request = { 'child_order_state': 'COMPLETED', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder() requires a `symbol` argument'); } const orders = await this.fetchOrders (symbol); const ordersById = this.indexBy (orders, 'id'); if (id in ordersById) { return ordersById[id]; } throw new OrderNotFound (this.id + ' No order found with id ' + id); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'product_code': market['id'], }; if (limit !== undefined) { request['count'] = limit; } const response = await this.privateGetGetexecutions (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); if (code !== 'JPY' && code !== 'USD' && code !== 'EUR') { throw new ExchangeError (this.id + ' allows withdrawing JPY, USD, EUR only, ' + code + ' is not supported'); } const currency = this.currency (code); const request = { 'currency_code': currency['id'], 'amount': amount, // 'bank_account_id': 1234, }; const response = await this.privatePostWithdraw (this.extend (request, params)); const id = this.safeString (response, 'message_id'); return { 'info': response, 'id': id, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/' + this.version + '/'; if (api === 'private') { request += 'me/'; } request += path; if (method === 'GET') { if (Object.keys (params).length) { request += '?' + this.urlencode (params); } } const url = this.urls['api'] + request; if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); let auth = [ nonce, method, request ].join (''); if (Object.keys (params).length) { if (method !== 'GET') { body = this.json (params); auth += body; } } headers = { 'ACCESS-KEY': this.apiKey, 'ACCESS-TIMESTAMP': nonce, 'ACCESS-SIGN': this.hmac (this.encode (auth), this.encode (this.secret)), 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],34:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, OrderNotFound, InsufficientFunds, DDoSProtection } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitforex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitforex', 'name': 'Bitforex', 'countries': [ 'CN' ], 'version': 'v1', 'has': { 'fetchBalance': true, 'fetchMarkets': true, 'createOrder': true, 'cancelOrder': true, 'fetchTicker': true, 'fetchTickers': false, 'fetchMyTrades': false, 'fetchTrades': true, 'fetchOrder': true, 'fetchOrders': false, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchOHLCV': true, }, 'timeframes': { '1m': '1min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '2h': '2hour', '4h': '4hour', '12h': '12hour', '1d': '1day', '1w': '1week', '1M': '1month', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/44310033-69e9e600-a3d8-11e8-873d-54d74d1bc4e4.jpg', 'api': 'https://api.bitforex.com', 'www': 'https://www.bitforex.com', 'doc': 'https://github.com/githubdev2020/API_Doc_en/wiki', 'fees': 'https://help.bitforex.com/en_us/?cat=13', 'referral': 'https://www.bitforex.com/en/invitationRegister?inviterId=1867438', }, 'api': { 'public': { 'get': [ 'api/v1/market/symbols', 'api/v1/market/ticker', 'api/v1/market/depth', 'api/v1/market/trades', 'api/v1/market/kline', ], }, 'private': { 'post': [ 'api/v1/fund/mainAccount', 'api/v1/fund/allAccount', 'api/v1/trade/placeOrder', 'api/v1/trade/placeMultiOrder', 'api/v1/trade/cancelOrder', 'api/v1/trade/orderInfo', 'api/v1/trade/orderInfos', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, 'funding': { 'tierBased': false, 'percentage': true, 'deposit': {}, 'withdraw': { 'BTC': 0.0005, 'ETH': 0.01, 'BCH': 0.0001, 'LTC': 0.001, 'ETC': 0.005, 'USDT': 5, 'CMCT': 30, 'AION': 3, 'LVT': 0, 'DATA': 40, 'RHP': 50, 'NEO': 0, 'AIDOC': 10, 'BQT': 2, 'R': 2, 'DPY': 0.8, 'GTC': 40, 'AGI': 30, 'DENT': 100, 'SAN': 1, 'SPANK': 8, 'AID': 5, 'OMG': 0.1, 'BFT': 5, 'SHOW': 150, 'TRX': 20, 'ABYSS': 10, 'THM': 25, 'ZIL': 20, 'PPT': 0.2, 'WTC': 0.4, 'LRC': 7, 'BNT': 1, 'CTXC': 1, 'MITH': 20, 'TRUE': 4, 'LYM': 10, 'VEE': 100, 'AUTO': 200, 'REN': 50, 'TIO': 2.5, 'NGC': 1.5, 'PST': 10, 'CRE': 200, 'IPC': 5, 'PTT': 1000, 'XMCT': 20, 'ATMI': 40, 'TERN': 40, 'XLM': 0.01, 'ODE': 15, 'FTM': 100, 'RTE': 100, 'DCC': 100, 'IMT': 500, 'GOT': 3, 'EGT': 500, 'DACC': 1000, 'UBEX': 500, 'ABL': 100, 'OLT': 100, 'DAV': 40, 'THRT': 10, 'RMESH': 3, 'UPP': 20, 'SDT': 0, 'SHR': 10, 'MTV': 3, 'ESS': 100, 'MET': 3, 'TTC': 20, 'LXT': 10, 'XCLP': 100, 'LUK': 100, 'UBC': 100, 'DTX': 10, 'BEAT': 20, 'DEED': 2, 'BGX': 3000, 'PRL': 20, 'ELY': 50, 'CARD': 300, 'SQR': 15, 'VRA': 400, 'BWX': 3500, 'MAS': 75, 'FLP': 0.6, 'UNC': 300, 'CRNC': 15, 'MFG': 70, 'ZXC': 70, 'TRT': 30, 'ZIX': 35, 'XRA': 10, 'AMO': 1600, 'IPG': 3, 'uDoo': 50, 'URB': 30, 'ARCONA': 3, 'CRAD': 5, 'NOBS': 1000, 'ADF': 2, 'ELF': 5, 'LX': 20, 'PATH': 15, 'SILK': 120, 'SKYFT': 50, 'EDN': 50, 'ADE': 50, 'EDR': 10, 'TIME': 0.25, 'SPRK': 20, 'QTUM': 0.01, 'BF': 5, 'ZPR': 100, 'HYB': 10, 'CAN': 30, 'CEL': 10, 'ATS': 50, 'KCASH': 1, 'ACT': 0.01, 'MT': 300, 'DXT': 30, 'WAB': 4000, 'HYDRO': 400, 'LQD': 5, 'OPTC': 200, 'EQUAD': 80, 'LATX': 50, 'LEDU': 100, 'RIT': 70, 'ACDC': 500, 'FSN': 2, }, }, }, 'commonCurrencies': { 'UOS': 'UOS Network', }, 'exceptions': { '4004': OrderNotFound, '1013': AuthenticationError, '1016': AuthenticationError, '3002': InsufficientFunds, '10204': DDoSProtection, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetApiV1MarketSymbols (params); const data = response['data']; const result = []; for (let i = 0; i < data.length; i++) { const market = data[i]; const id = this.safeString (market, 'symbol'); const symbolParts = id.split ('-'); const baseId = symbolParts[2]; const quoteId = symbolParts[1]; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const active = true; const precision = { 'amount': this.safeInteger (market, 'amountPrecision'), 'price': this.safeInteger (market, 'pricePrecision'), }; const limits = { 'amount': { 'min': this.safeFloat (market, 'minOrderAmount'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } parseTrade (trade, market = undefined) { let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger (trade, 'time'); const id = this.safeString (trade, 'tid'); const orderId = undefined; const amount = this.safeFloat (trade, 'amount'); const price = this.safeFloat (trade, 'price'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } const sideId = this.safeInteger (trade, 'direction'); const side = this.parseSide (sideId); return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'order': orderId, 'fee': undefined, 'takerOrMaker': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; if (limit !== undefined) { request['size'] = limit; } const market = this.market (symbol); const response = await this.publicGetApiV1MarketTrades (this.extend (request, params)); return this.parseTrades (response['data'], market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostApiV1FundAllAccount (params); const data = response['data']; const result = { 'info': response }; for (let i = 0; i < data.length; i++) { const balance = data[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['used'] = this.safeFloat (balance, 'frozen'); account['free'] = this.safeFloat (balance, 'active'); account['total'] = this.safeFloat (balance, 'fix'); result[code] = account; } return this.parseBalance (result); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.markets[symbol]; const request = { 'symbol': market['id'], }; const response = await this.publicGetApiV1MarketTicker (this.extend (request, params)); const data = response['data']; const timestamp = this.safeInteger (data, 'date'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (data, 'high'), 'low': this.safeFloat (data, 'low'), 'bid': this.safeFloat (data, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (data, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': this.safeFloat (data, 'last'), 'last': this.safeFloat (data, 'last'), 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (data, 'vol'), 'quoteVolume': undefined, 'info': response, }; } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ this.safeInteger (ohlcv, 'time'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'vol'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'ktype': this.timeframes[timeframe], }; if (limit !== undefined) { request['size'] = limit; // default 1, max 600 } const response = await this.publicGetApiV1MarketKline (this.extend (request, params)); const ohlcvs = this.safeValue (response, 'data', []); return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const marketId = this.marketId (symbol); const request = { 'symbol': marketId, }; if (limit !== undefined) { request['size'] = limit; } const response = await this.publicGetApiV1MarketDepth (this.extend (request, params)); const data = this.safeValue (response, 'data'); const timestamp = this.safeInteger (response, 'time'); return this.parseOrderBook (data, timestamp, 'bids', 'asks', 'price', 'amount'); } parseOrderStatus (status) { const statuses = { '0': 'open', '1': 'open', '2': 'closed', '3': 'canceled', '4': 'canceled', }; return (status in statuses) ? statuses[status] : status; } parseSide (sideId) { if (sideId === 1) { return 'buy'; } else if (sideId === 2) { return 'sell'; } else { return undefined; } } parseOrder (order, market = undefined) { const id = this.safeString (order, 'orderId'); const timestamp = this.safeFloat (order, 'createTime'); const lastTradeTimestamp = this.safeFloat (order, 'lastTime'); const symbol = market['symbol']; const sideId = this.safeInteger (order, 'tradeType'); const side = this.parseSide (sideId); const type = undefined; const price = this.safeFloat (order, 'orderPrice'); const average = this.safeFloat (order, 'avgPrice'); const amount = this.safeFloat (order, 'orderAmount'); const filled = this.safeFloat (order, 'dealAmount'); const remaining = amount - filled; const status = this.parseOrderStatus (this.safeString (order, 'orderState')); const cost = filled * price; const feeSide = (side === 'buy') ? 'base' : 'quote'; const feeCurrency = market[feeSide]; const fee = { 'cost': this.safeFloat (order, 'tradeFee'), 'currency': feeCurrency, }; const result = { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; return result; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': this.marketId (symbol), 'orderId': id, }; const response = await this.privatePostApiV1TradeOrderInfo (this.extend (request, params)); const order = this.parseOrder (response['data'], market); return order; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': this.marketId (symbol), 'state': 0, }; const response = await this.privatePostApiV1TradeOrderInfos (this.extend (request, params)); return this.parseOrders (response['data'], market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': this.marketId (symbol), 'state': 1, }; const response = await this.privatePostApiV1TradeOrderInfos (this.extend (request, params)); return this.parseOrders (response['data'], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); let sideId = undefined; if (side === 'buy') { sideId = 1; } else if (side === 'sell') { sideId = 2; } const request = { 'symbol': this.marketId (symbol), 'price': price, 'amount': amount, 'tradeType': sideId, }; const response = await this.privatePostApiV1TradePlaceOrder (this.extend (request, params)); const data = response['data']; return { 'info': response, 'id': this.safeString (data, 'orderId'), }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; if (symbol !== undefined) { request['symbol'] = this.marketId (symbol); } const results = await this.privatePostApiV1TradeCancelOrder (this.extend (request, params)); const success = results['success']; const returnVal = { 'info': results, 'success': success }; return returnVal; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); let payload = this.urlencode ({ 'accessKey': this.apiKey }); query['nonce'] = this.milliseconds (); if (Object.keys (query).length) { payload += '&' + this.urlencode (this.keysort (query)); } // let message = '/' + 'api/' + this.version + '/' + path + '?' + payload; const message = '/' + path + '?' + payload; const signature = this.hmac (this.encode (message), this.encode (this.secret)); body = payload + '&signData=' + signature; headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (typeof body !== 'string') { return; // fallback to default error handler } if ((body[0] === '{') || (body[0] === '[')) { const feedback = this.id + ' ' + body; const success = this.safeValue (response, 'success'); if (success !== undefined) { if (!success) { const code = this.safeString (response, 'code'); this.throwExactlyMatchedException (this.exceptions, code, feedback); throw new ExchangeError (feedback); } } } } }; },{"./base/Exchange":7,"./base/errors":9}],35:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ExchangeNotAvailable, AuthenticationError, BadRequest, PermissionDenied, InvalidAddress, ArgumentsRequired, InvalidOrder } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bithumb extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bithumb', 'name': 'Bithumb', 'countries': [ 'KR' ], // South Korea 'rateLimit': 500, 'has': { 'CORS': true, 'createOrder': true, 'cancelOrder': true, 'createMarketOrder': true, 'fetchTickers': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/30597177-ea800172-9d5e-11e7-804c-b9d4fa9b56b0.jpg', 'api': { 'public': 'https://api.bithumb.com/public', 'private': 'https://api.bithumb.com', }, 'www': 'https://www.bithumb.com', 'doc': 'https://apidocs.bithumb.com', 'fees': 'https://en.bithumb.com/customer_support/info_fee', }, 'api': { 'public': { 'get': [ 'ticker/{currency}', 'ticker/all', 'orderbook/{currency}', 'orderbook/all', 'transaction_history/{currency}', 'transaction_history/all', ], }, 'private': { 'post': [ 'info/account', 'info/balance', 'info/wallet_address', 'info/ticker', 'info/orders', 'info/user_transactions', 'info/order_detail', 'trade/place', 'trade/cancel', 'trade/btc_withdrawal', 'trade/krw_deposit', 'trade/krw_withdrawal', 'trade/market_buy', 'trade/market_sell', ], }, }, 'fees': { 'trading': { 'maker': 0.25 / 100, 'taker': 0.25 / 100, }, }, 'exceptions': { 'Bad Request(SSL)': BadRequest, 'Bad Request(Bad Method)': BadRequest, 'Bad Request.(Auth Data)': AuthenticationError, // { "status": "5100", "message": "Bad Request.(Auth Data)" } 'Not Member': AuthenticationError, 'Invalid Apikey': AuthenticationError, // {"status":"5300","message":"Invalid Apikey"} 'Method Not Allowed.(Access IP)': PermissionDenied, 'Method Not Allowed.(BTC Adress)': InvalidAddress, 'Method Not Allowed.(Access)': PermissionDenied, 'Database Fail': ExchangeNotAvailable, 'Invalid Parameter': BadRequest, '5600': ExchangeError, 'Unknown Error': ExchangeError, 'After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions': ExchangeError, // {"status":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"} }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetTickerAll (params); const data = this.safeValue (response, 'data'); const currencyIds = Object.keys (data); const result = []; for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; if (currencyId === 'date') { continue; } const market = data[currencyId]; const base = currencyId; const quote = 'KRW'; const symbol = currencyId + '/' + quote; let active = true; if (Array.isArray (market)) { const numElements = market.length; if (numElements === 0) { active = false; } } result.push ({ 'id': currencyId, 'symbol': symbol, 'base': base, 'quote': quote, 'info': market, 'active': active, 'precision': { 'amount': undefined, 'price': undefined, }, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'baseId': undefined, 'quoteId': undefined, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const request = { 'currency': 'ALL', }; const response = await this.privatePostInfoBalance (this.extend (request, params)); const result = { 'info': response }; const balances = this.safeValue (response, 'data'); const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const account = this.account (); const currency = this.currency (code); const lowerCurrencyId = this.safeStringLower (currency, 'id'); account['total'] = this.safeFloat (balances, 'total_' + lowerCurrencyId); account['used'] = this.safeFloat (balances, 'in_use_' + lowerCurrencyId); account['free'] = this.safeFloat (balances, 'available_' + lowerCurrencyId); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['base'], }; if (limit !== undefined) { request['count'] = limit; // default 30, max 30 } const response = await this.publicGetOrderbookCurrency (this.extend (request, params)); // // { // "status":"0000", // "data":{ // "timestamp":"1587621553942", // "payment_currency":"KRW", // "order_currency":"BTC", // "bids":[ // {"price":"8652000","quantity":"0.0043"}, // {"price":"8651000","quantity":"0.0049"}, // {"price":"8650000","quantity":"8.4791"}, // ], // "asks":[ // {"price":"8654000","quantity":"0.119"}, // {"price":"8655000","quantity":"0.254"}, // {"price":"8658000","quantity":"0.119"}, // ] // } // } // const data = this.safeValue (response, 'data', {}); const timestamp = this.safeInteger (data, 'timestamp'); return this.parseOrderBook (data, timestamp, 'bids', 'asks', 'price', 'quantity'); } parseTicker (ticker, market = undefined) { // // fetchTicker, fetchTickers // // { // "opening_price":"227100", // "closing_price":"228400", // "min_price":"222300", // "max_price":"230000", // "units_traded":"82618.56075337", // "acc_trade_value":"18767376138.6031", // "prev_closing_price":"227100", // "units_traded_24H":"151871.13484676", // "acc_trade_value_24H":"34247610416.8974", // "fluctate_24H":"8700", // "fluctate_rate_24H":"3.96", // "date":"1587710327264", // fetchTickers inject this // } // const timestamp = this.safeInteger (ticker, 'date'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const open = this.safeFloat (ticker, 'opening_price'); const close = this.safeFloat (ticker, 'closing_price'); let change = undefined; let percentage = undefined; let average = undefined; if ((close !== undefined) && (open !== undefined)) { change = close - open; if (open > 0) { percentage = change / open * 100; } average = this.sum (open, close) / 2; } const baseVolume = this.safeFloat (ticker, 'units_traded_24H'); const quoteVolume = this.safeFloat (ticker, 'acc_trade_value_24H'); let vwap = undefined; if (quoteVolume !== undefined && baseVolume !== undefined) { vwap = quoteVolume / baseVolume; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'max_price'), 'low': this.safeFloat (ticker, 'min_price'), 'bid': this.safeFloat (ticker, 'buy_price'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell_price'), 'askVolume': undefined, 'vwap': vwap, 'open': open, 'close': close, 'last': close, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickerAll (params); // // { // "status":"0000", // "data":{ // "BTC":{ // "opening_price":"9045000", // "closing_price":"9132000", // "min_price":"8938000", // "max_price":"9168000", // "units_traded":"4619.79967497", // "acc_trade_value":"42021363832.5187", // "prev_closing_price":"9041000", // "units_traded_24H":"8793.5045804", // "acc_trade_value_24H":"78933458515.4962", // "fluctate_24H":"530000", // "fluctate_rate_24H":"6.16" // }, // "date":"1587710878669" // } // } // const result = {}; const data = this.safeValue (response, 'data', {}); const timestamp = this.safeInteger (data, 'date'); const tickers = this.omit (data, 'date'); const ids = Object.keys (tickers); for (let i = 0; i < ids.length; i++) { const id = ids[i]; let symbol = id; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } const ticker = tickers[id]; const isArray = Array.isArray (ticker); if (!isArray) { ticker['date'] = timestamp; result[symbol] = this.parseTicker (ticker, market); } } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['base'], }; const response = await this.publicGetTickerCurrency (this.extend (request, params)); // // { // "status":"0000", // "data":{ // "opening_price":"227100", // "closing_price":"228400", // "min_price":"222300", // "max_price":"230000", // "units_traded":"82618.56075337", // "acc_trade_value":"18767376138.6031", // "prev_closing_price":"227100", // "units_traded_24H":"151871.13484676", // "acc_trade_value_24H":"34247610416.8974", // "fluctate_24H":"8700", // "fluctate_rate_24H":"3.96", // "date":"1587710327264" // } // } // const data = this.safeValue (response, 'data', {}); return this.parseTicker (data, market); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "transaction_date":"2020-04-23 22:21:46", // "type":"ask", // "units_traded":"0.0125", // "price":"8667000", // "total":"108337" // } // // fetchOrder (private) // // { // "transaction_date": "1572497603902030", // "price": "8601000", // "units": "0.005", // "fee_currency": "KRW", // "fee": "107.51", // "total": "43005" // } // // a workaround for their bug in date format, hours are not 0-padded let timestamp = undefined; const transactionDatetime = this.safeString (trade, 'transaction_date'); if (transactionDatetime !== undefined) { const parts = transactionDatetime.split (' '); const numParts = parts.length; if (numParts > 1) { const transactionDate = parts[0]; let transactionTime = parts[1]; if (transactionTime.length < 8) { transactionTime = '0' + transactionTime; } timestamp = this.parse8601 (transactionDate + ' ' + transactionTime); } else { timestamp = this.safeIntegerProduct (trade, 'transaction_date', 0.001); } } if (timestamp !== undefined) { timestamp -= 9 * 3600000; // they report UTC + 9 hours, server in Korean timezone } const type = undefined; let side = this.safeString (trade, 'type'); side = (side === 'ask') ? 'sell' : 'buy'; const id = this.safeString (trade, 'cont_no'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'units_traded'); let cost = this.safeFloat (trade, 'total'); if (cost === undefined) { if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } } let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'fee_currency'); const feeCurrencyCode = this.commonCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['base'], }; if (limit === undefined) { request['count'] = limit; // default 20, max 100 } const response = await this.publicGetTransactionHistoryCurrency (this.extend (request, params)); // // { // "status":"0000", // "data":[ // { // "transaction_date":"2020-04-23 22:21:46", // "type":"ask", // "units_traded":"0.0125", // "price":"8667000", // "total":"108337" // }, // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTrades (data, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_currency': market['id'], 'Payment_currency': market['quote'], 'units': amount, }; let method = 'privatePostTradePlace'; if (type === 'limit') { request['price'] = price; request['type'] = (side === 'buy') ? 'bid' : 'ask'; } else { method = 'privatePostTradeMarket' + this.capitalize (side); } const response = await this[method] (this.extend (request, params)); const id = this.safeString (response, 'order_id'); if (id === undefined) { throw new InvalidOrder (this.id + ' createOrder did not return an order id'); } return { 'info': response, 'symbol': symbol, 'type': type, 'side': side, 'id': id, }; } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_id': id, 'count': 1, 'order_currency': market['base'], 'payment_currency': market['quote'], }; const response = await this.privatePostInfoOrderDetail (this.extend (request, params)); // // { // "status": "0000", // "data": { // "transaction_date": "1572497603668315", // "type": "bid", // "order_status": "Completed", // "order_currency": "BTC", // "payment_currency": "KRW", // "order_price": "8601000", // "order_qty": "0.007", // "cancel_date": "", // "cancel_type": "", // "contract": [ // { // "transaction_date": "1572497603902030", // "price": "8601000", // "units": "0.005", // "fee_currency": "KRW", // "fee": "107.51", // "total": "43005" // }, // ] // } // } // const data = this.safeValue (response, 'data'); return this.parseOrder (this.extend (data, { 'order_id': id }), market); } parseOrderStatus (status) { const statuses = { 'Pending': 'open', 'Completed': 'closed', 'Cancel': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // fetchOrder // // { // "transaction_date": "1572497603668315", // "type": "bid", // "order_status": "Completed", // "order_currency": "BTC", // "payment_currency": "KRW", // "order_price": "8601000", // "order_qty": "0.007", // "cancel_date": "", // "cancel_type": "", // "contract": [ // { // "transaction_date": "1572497603902030", // "price": "8601000", // "units": "0.005", // "fee_currency": "KRW", // "fee": "107.51", // "total": "43005" // }, // ] // } // // fetchOpenOrders // // { // "order_currency": "BTC", // "payment_currency": "KRW", // "order_id": "C0101000007408440032", // "order_date": "1571728739360570", // "type": "bid", // "units": "5.0", // "units_remaining": "5.0", // "price": "501000", // } // const timestamp = this.safeIntegerProduct (order, 'order_date', 0.001); const sideProperty = this.safeValue2 (order, 'type', 'side'); const side = (sideProperty === 'bid') ? 'buy' : 'sell'; const status = this.parseOrderStatus (this.safeString (order, 'order_status')); let price = this.safeFloat2 (order, 'order_price', 'price'); let type = 'limit'; if (price === 0) { price = undefined; type = 'market'; } const amount = this.safeFloat2 (order, 'order_qty', 'units'); let remaining = this.safeFloat (order, 'units_remaining'); if (remaining === undefined) { if (status === 'closed') { remaining = 0; } else { remaining = amount; } } let filled = undefined; if ((amount !== undefined) && (remaining !== undefined)) { filled = amount - remaining; } let symbol = undefined; const baseId = this.safeString (order, 'order_currency'); const quoteId = this.safeString (order, 'payment_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); if ((base !== undefined) && (quote !== undefined)) { symbol = base + '/' + quote; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const rawTrades = this.safeValue (order, 'contract'); let trades = undefined; const id = this.safeString (order, 'order_id'); if (rawTrades !== undefined) { trades = this.parseTrades (rawTrades, market, undefined, undefined, { 'side': side, 'symbol': symbol, 'order': id, }); } return { 'info': order, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': undefined, 'average': undefined, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': trades, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 100; } const request = { 'count': limit, 'order_currency': market['base'], 'payment_currency': market['quote'], }; if (since !== undefined) { request['after'] = since; } const response = await this.privatePostInfoOrders (this.extend (request, params)); // // { // "status": "0000", // "data": [ // { // "order_currency": "BTC", // "payment_currency": "KRW", // "order_id": "C0101000007408440032", // "order_date": "1571728739360570", // "type": "bid", // "units": "5.0", // "units_remaining": "5.0", // "price": "501000", // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOrders (data, market, since, limit); } async cancelOrder (id, symbol = undefined, params = {}) { const side_in_params = ('side' in params); if (!side_in_params) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument and a `side` parameter (sell or buy)'); } if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument and a `side` parameter (sell or buy)'); } const market = this.market (symbol); const side = (params['side'] === 'buy') ? 'bid' : 'ask'; params = this.omit (params, [ 'side', 'currency' ]); // https://github.com/ccxt/ccxt/issues/6771 const request = { 'order_id': id, 'type': side, 'order_currency': market['base'], 'payment_currency': market['quote'], }; return await this.privatePostTradeCancel (this.extend (request, params)); } cancelUnifiedOrder (order, params = {}) { const request = { 'side': order['side'], }; return this.cancelOrder (order['id'], order['symbol'], this.extend (request, params)); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'units': amount, 'address': address, 'currency': currency['id'], }; if (currency === 'XRP' || currency === 'XMR') { const destination = this.safeString (params, 'destination'); if ((tag === undefined) && (destination === undefined)) { throw new ArgumentsRequired (this.id + ' ' + code + ' withdraw() requires a tag argument or an extra destination param'); } else if (tag !== undefined) { request['destination'] = tag; } } const response = await this.privatePostTradeBtcWithdrawal (this.extend (request, params)); return { 'info': response, 'id': undefined, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const endpoint = '/' + this.implodeParams (path, params); let url = this.urls['api'][api] + endpoint; const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); body = this.urlencode (this.extend ({ 'endpoint': endpoint, }, query)); const nonce = this.nonce ().toString (); const auth = endpoint + "\0" + body + "\0" + nonce; // eslint-disable-line quotes const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); const signature64 = this.decode (this.stringToBase64 (this.encode (signature))); headers = { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', 'Api-Key': this.apiKey, 'Api-Sign': signature64.toString (), 'Api-Nonce': nonce, }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('status' in response) { // // {"status":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"} // const status = this.safeString (response, 'status'); const message = this.safeString (response, 'message'); if (status !== undefined) { if (status === '0000') { return; // no error } const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, status, feedback); this.throwExactlyMatchedException (this.exceptions, message, feedback); throw new ExchangeError (feedback); } } } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('status' in response) { if (response['status'] === '0000') { return response; } throw new ExchangeError (this.id + ' ' + this.json (response)); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],36:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const zb = require ('./zb.js'); // --------------------------------------------------------------------------- module.exports = class bitkk extends zb { describe () { return this.deepExtend (super.describe (), { 'id': 'bitkk', 'name': 'bitkk', 'comment': 'a Chinese ZB clone', 'urls': { 'api': { 'public': 'http://api.bitkk.com/data', // no https for public API 'private': 'https://trade.bitkk.com/api', }, 'www': 'https://www.bitkk.com', 'doc': 'https://www.bitkk.com/i/developer', 'fees': 'https://www.bitkk.com/i/rate', }, }); } }; },{"./zb.js":172}],37:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { AuthenticationError, ArgumentsRequired, ExchangeError, InvalidOrder, BadRequest, OrderNotFound, DDoSProtection, BadSymbol } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitmart extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitmart', 'name': 'BitMart', 'countries': [ 'US', 'CN', 'HK', 'KR' ], 'rateLimit': 1000, 'version': 'v2', 'has': { 'CORS': true, 'fetchMarkets': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchCurrencies': true, 'fetchOrderBook': true, 'fetchTrades': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchBalance': true, 'createOrder': true, 'cancelOrder': true, 'cancelAllOrders': true, 'fetchOrders': false, 'fetchOrderTrades': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchCanceledOrders': true, 'fetchOrder': true, 'signIn': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg', 'api': 'https://openapi.bitmart.com', 'www': 'https://www.bitmart.com/', 'doc': 'https://github.com/bitmartexchange/bitmart-official-api-docs', 'referral': 'http://www.bitmart.com/?r=rQCFLh', 'fees': 'https://www.bitmart.com/fee/en', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': true, }, 'api': { 'token': { 'post': [ 'authentication', ], }, 'public': { 'get': [ 'currencies', 'ping', 'steps', 'symbols', 'symbols_details', 'symbols/{symbol}/kline', 'symbols/{symbol}/orders', 'symbols/{symbol}/trades', 'ticker', 'time', ], }, 'private': { 'get': [ 'orders', 'orders/{id}', 'trades', 'wallet', ], 'post': [ 'orders', ], 'delete': [ 'orders', 'orders/{id}', ], }, }, 'timeframes': { '1m': 1, '3m': 3, '5m': 5, '15m': 15, '30m': 30, '45m': 45, '1h': 60, '2h': 120, '3h': 180, '4h': 240, '1d': 1440, '1w': 10080, '1M': 43200, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': 0.002, 'maker': 0.001, 'tiers': { 'taker': [ [0, 0.20 / 100], [10, 0.18 / 100], [50, 0.16 / 100], [250, 0.14 / 100], [1000, 0.12 / 100], [5000, 0.10 / 100], [25000, 0.08 / 100], [50000, 0.06 / 100], ], 'maker': [ [0, 0.1 / 100], [10, 0.09 / 100], [50, 0.08 / 100], [250, 0.07 / 100], [1000, 0.06 / 100], [5000, 0.05 / 100], [25000, 0.04 / 100], [50000, 0.03 / 100], ], }, }, }, 'exceptions': { 'exact': { 'Place order error': InvalidOrder, // {"message":"Place order error"} 'Not found': OrderNotFound, // {"message":"Not found"} 'Visit too often, please try again later': DDoSProtection, // {"code":-30,"msg":"Visit too often, please try again later","subMsg":"","data":{}} 'Unknown symbol': BadSymbol, // {"message":"Unknown symbol"} }, 'broad': { 'Invalid limit. limit must be in the range': InvalidOrder, 'Maximum price is': InvalidOrder, // {"message":"Maximum price is 0.112695"} // {"message":"Required Integer parameter 'status' is not present"} // {"message":"Required String parameter 'symbol' is not present"} // {"message":"Required Integer parameter 'offset' is not present"} // {"message":"Required Integer parameter 'limit' is not present"} // {"message":"Required Long parameter 'from' is not present"} // {"message":"Required Long parameter 'to' is not present"} 'is not present': BadRequest, }, }, 'commonCurrencies': { 'ONE': 'Menlo One', 'PLA': 'Plair', }, }); } async fetchTime (params = {}) { const response = await this.publicGetTime (params); // // { // "server_time": 1527777538000 // } // return this.safeInteger (response, 'server_time'); } async signIn (params = {}) { const message = this.apiKey + ':' + this.secret + ':' + this.uid; const data = { 'grant_type': 'client_credentials', 'client_id': this.apiKey, 'client_secret': this.hmac (this.encode (message), this.encode (this.secret), 'sha256'), }; const response = await this.tokenPostAuthentication (this.extend (data, params)); const accessToken = this.safeString (response, 'access_token'); if (!accessToken) { throw new AuthenticationError (this.id + ' signIn() failed to authenticate. Access token missing from response.'); } const expiresIn = this.safeInteger (response, 'expires_in'); this.options['expires'] = this.sum (this.nonce (), expiresIn * 1000); this.options['accessToken'] = accessToken; return response; } async fetchMarkets (params = {}) { const markets = await this.publicGetSymbolsDetails (params); // // [ // { // "id":"1SG_BTC", // "base_currency":"1SG", // "quote_currency":"BTC", // "quote_increment":"0.1", // "base_min_size":"0.1000000000", // "base_max_size":"10000000.0000000000", // "price_min_precision":4, // "price_max_precision":6, // "expiration":"NA" // } // ] // const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'id'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; // // https://github.com/bitmartexchange/bitmart-official-api-docs/blob/master/rest/public/symbols_details.md#response-details // from the above API doc: // quote_increment Minimum order price as well as the price increment // price_min_precision Minimum price precision (digit) used to query price and kline // price_max_precision Maximum price precision (digit) used to query price and kline // // the docs are wrong: https://github.com/ccxt/ccxt/issues/5612 // const quoteIncrement = this.safeString (market, 'quote_increment'); const amountPrecision = this.precisionFromString (quoteIncrement); const pricePrecision = this.safeInteger (market, 'price_max_precision'); const precision = { 'amount': amountPrecision, 'price': pricePrecision, }; const limits = { 'amount': { 'min': this.safeFloat (market, 'base_min_size'), 'max': this.safeFloat (market, 'base_max_size'), }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'limits': limits, 'info': market, 'active': undefined, }); } return result; } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); const marketId = this.safeString (ticker, 'symbol_id'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else if (marketId !== undefined) { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } const last = this.safeFloat (ticker, 'current_price'); const percentage = this.safeFloat (ticker, 'fluctuation'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'highest_price'), 'low': this.safeFloat (ticker, 'lowest_price'), 'bid': this.safeFloat (ticker, 'bid_1'), 'bidVolume': this.safeFloat (ticker, 'bid_1_amount'), 'ask': this.safeFloat (ticker, 'ask_1'), 'askVolume': this.safeFloat (ticker, 'ask_1_amount'), 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': percentage * 100, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': this.safeFloat (ticker, 'base_volume'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; const response = await this.publicGetTicker (this.extend (request, params)); // // { // "volume":"97487.38", // "ask_1":"0.00148668", // "base_volume":"144.59", // "lowest_price":"0.00144362", // "bid_1":"0.00148017", // "highest_price":"0.00151000", // "ask_1_amount":"92.03", // "current_price":"0.00148230", // "fluctuation":"+0.0227", // "symbol_id":"XRP_ETH", // "url":"https://www.bitmart.com/trade?symbol=XRP_ETH", // "bid_1_amount":"134.78" // } // return this.parseTicker (response); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const tickers = await this.publicGetTicker (params); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = this.parseTicker (tickers[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchCurrencies (params = {}) { const currencies = await this.publicGetCurrencies (params); // // [ // { // "name":"CNY1", // "withdraw_enabled":false, // "id":"CNY1", // "deposit_enabled":false // } // ] // const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const currencyId = this.safeString (currency, 'id'); const code = this.safeCurrencyCode (currencyId); const name = this.safeString (currency, 'name'); const withdrawEnabled = this.safeValue (currency, 'withdraw_enabled'); const depositEnabled = this.safeValue (currency, 'deposit_enabled'); const active = withdrawEnabled && depositEnabled; result[code] = { 'id': currencyId, 'code': code, 'name': name, 'info': currency, // the original payload 'active': active, 'fee': undefined, 'precision': undefined, 'limits': { 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, 'withdraw': { 'min': undefined, 'max': undefined }, }, }; } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), // 'precision': 4, // optional price precision / depth level whose range is defined in symbol details }; const response = await this.publicGetSymbolsSymbolOrders (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'buys', 'sells', 'price', 'amount'); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "amount":"2.29275119", // "price":"0.021858", // "count":"104.8930", // "order_time":1563997286061, // "type":"sell" // } // // fetchMyTrades (private) // // { // active: true, // amount: '0.2000', // entrustType: 1, // entrust_id: 979648824, // fees: '0.0000085532', // price: '0.021383', // symbol: 'ETH_BTC', // timestamp: 1574343514000, // trade_id: 329418828 // }, // const id = this.safeString (trade, 'trade_id'); const timestamp = this.safeInteger2 (trade, 'timestamp', 'order_time'); const type = undefined; let side = this.safeStringLower (trade, 'type'); if ((side === undefined) && ('entrustType' in trade)) { side = trade['entrustType'] ? 'sell' : 'buy'; } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } const orderId = this.safeInteger (trade, 'entrust_id'); const marketId = this.safeString (trade, 'symbol'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const feeCost = this.safeFloat (trade, 'fees'); let fee = undefined; if (feeCost !== undefined) { let feeCurrencyCode = undefined; if (market !== undefined) { feeCurrencyCode = (side === 'buy') ? market['base'] : market['quote']; } fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return { 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'takerOrMaker': undefined, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetSymbolsSymbolTrades (this.extend (request, params)); // // [ // { // "amount":"2.29275119", // "price":"0.021858", // "count":"104.8930", // "order_time":1563997286061, // "type":"sell" // } // ] // return this.parseTrades (response, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); // limit is required, must be in the range (0, 50) const maxLimit = 50; limit = (limit === undefined) ? maxLimit : Math.min (limit, maxLimit); const request = { 'symbol': market['id'], 'offset': 0, // current page, starts from 0 'limit': limit, // required }; const response = await this.privateGetTrades (this.extend (request, params)); // // { // "total_trades": 216, // "total_pages": 22, // "current_page": 0, // "trades": [ // { // "symbol": "BMX_ETH", // "amount": "1.0", // "fees": "0.0005000000", // "trade_id": 2734956, // "price": "0.00013737", // "active": true, // "entrust_id": 5576623, // "timestamp": 1545292334000 // }, // ] // } // const trades = this.safeValue (response, 'trades', []); return this.parseTrades (trades, market, since, limit); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'entrust_id': id, }; return await this.fetchMyTrades (symbol, since, limit, this.extend (request, params)); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ this.safeInteger (ohlcv, 'timestamp'), this.safeFloat (ohlcv, 'open_price'), this.safeFloat (ohlcv, 'highest_price'), this.safeFloat (ohlcv, 'lowest_price'), this.safeFloat (ohlcv, 'current_price'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { if (since === undefined && limit === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV requires either a `since` argument or a `limit` argument (or both)'); } await this.loadMarkets (); const market = this.market (symbol); const periodInSeconds = this.parseTimeframe (timeframe); const duration = periodInSeconds * limit * 1000; let to = this.milliseconds (); if (since === undefined) { since = to - duration; } else { to = this.sum (since, duration); } const request = { 'symbol': market['id'], 'from': since, // start time of k-line data (in milliseconds, required) 'to': to, // end time of k-line data (in milliseconds, required) 'step': this.timeframes[timeframe], // steps of sampling (in minutes, default 1 minute, optional) }; const response = await this.publicGetSymbolsSymbolKline (this.extend (request, params)); // // [ // { // "timestamp":1525761000000, // "open_price":"0.010130", // "highest_price":"0.010130", // "lowest_price":"0.010130", // "current_price":"0.010130", // "volume":"0.000000" // } // ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const balances = await this.privateGetWallet (params); // // [ // { // "name":"Bitcoin", // "available":"0.0000000000", // "frozen":"0.0000000000", // "id":"BTC" // } // ] // const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'id'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'frozen'); result[code] = account; } return this.parseBalance (result); } parseOrder (order, market = undefined) { // // createOrder // // { // "entrust_id":1223181 // } // // cancelOrder // // {} // // fetchOrder, fetchOrdersByStatus, fetchOpenOrders, fetchClosedOrders // // { // "entrust_id":1223181, // "symbol":"BMX_ETH", // "timestamp":1528060666000, // "side":"buy", // "price":"1.000000", // "fees":"0.1", // "original_amount":"1", // "executed_amount":"1", // "remaining_amount":"0", // "status":3 // } // const id = this.safeString (order, 'entrust_id'); const timestamp = this.milliseconds (); const status = this.parseOrderStatus (this.safeString (order, 'status')); let symbol = undefined; const marketId = this.safeString (order, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'original_amount'); let cost = undefined; let filled = this.safeFloat (order, 'executed_amount'); let remaining = this.safeFloat (order, 'remaining_amount'); if (amount !== undefined) { if (remaining !== undefined) { if (filled === undefined) { filled = amount - remaining; } } if (filled !== undefined) { if (remaining === undefined) { remaining = amount - filled; } if (cost === undefined) { if (price !== undefined) { cost = price * filled; } } } } const side = this.safeString (order, 'side'); const type = undefined; return { 'id': id, 'clientOrderId': undefined, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': undefined, 'average': undefined, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } parseOrderStatus (status) { const statuses = { '0': 'all', '1': 'open', '2': 'open', '3': 'closed', '4': 'canceled', '5': 'open', '6': 'closed', }; return this.safeString (statuses, status, status); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type !== 'limit') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'side': side.toLowerCase (), 'amount': this.amountToPrecision (symbol, amount), 'price': this.priceToPrecision (symbol, price), }; const response = await this.privatePostOrders (this.extend (request, params)); // // { // "entrust_id":1223181 // } // return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const intId = parseInt (id); const request = { 'id': intId, 'entrust_id': intId, }; const response = await this.privateDeleteOrdersId (this.extend (request, params)); // // responds with an empty object {} // return this.parseOrder (response); } async cancelAllOrders (symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument'); } const side = this.safeString (params, 'side'); if (side === undefined) { throw new ArgumentsRequired (this.id + " cancelAllOrders requires a `side` parameter ('buy' or 'sell')"); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'side': side, // 'buy' or 'sell' }; const response = await this.privateDeleteOrders (this.extend (request, params)); // // responds with an empty object {} // return response; } async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrdersByStatus requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 500; // default 500, max 1000 } const request = { 'symbol': market['id'], 'status': status, 'offset': 0, // current page, starts from 0 'limit': limit, }; const response = await this.privateGetOrders (this.extend (request, params)); // // { // "orders":[ // { // "entrust_id":1223181, // "symbol":"BMX_ETH", // "timestamp":1528060666000, // "side":"buy", // "price":"1.000000", // "fees":"0.1", // "original_amount":"1", // "executed_amount":"1", // "remaining_amount":"0", // "status":3 // } // ], // "total_pages":1, // "total_orders":1, // "current_page":0, // } // const orders = this.safeValue (response, 'orders', []); return this.parseOrders (orders, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { // 5 = pending & partially filled orders return await this.fetchOrdersByStatus (5, symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { // 3 = closed orders return await this.fetchOrdersByStatus (3, symbol, since, limit, params); } async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { // 4 = canceled orders return await this.fetchOrdersByStatus (4, symbol, since, limit, params); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privateGetOrdersId (this.extend (request, params)); // // { // "entrust_id":1223181, // "symbol":"BMX_ETH", // "timestamp":1528060666000, // "side":"buy", // "price":"1.000000", // "fees":"0.1", // "original_amount":"1", // "executed_amount":"1", // "remaining_amount":"0", // "status":3 // } // return this.parseOrder (response); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); let query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'token') { this.checkRequiredCredentials (); body = this.urlencode (query); headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } else { const nonce = this.nonce (); this.checkRequiredCredentials (); const token = this.safeString (this.options, 'accessToken'); if (token === undefined) { throw new AuthenticationError (this.id + ' ' + path + ' endpoint requires an accessToken option or a prior call to signIn() method'); } const expires = this.safeInteger (this.options, 'expires'); if (expires !== undefined) { if (nonce >= expires) { throw new AuthenticationError (this.id + ' accessToken expired, supply a new accessToken or call the signIn() method'); } } if (Object.keys (query).length) { url += '?' + this.urlencode (query); } headers = { 'Content-Type': 'application/json', 'X-BM-TIMESTAMP': nonce.toString (), 'X-BM-AUTHORIZATION': 'Bearer ' + token, }; if (method !== 'GET') { query = this.keysort (query); body = this.json (query); const message = this.urlencode (query); headers['X-BM-SIGNATURE'] = this.hmac (this.encode (message), this.encode (this.secret), 'sha256'); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } // // {"message":"Maximum price is 0.112695"} // {"message":"Required Integer parameter 'status' is not present"} // {"message":"Required String parameter 'symbol' is not present"} // {"message":"Required Integer parameter 'offset' is not present"} // {"message":"Required Integer parameter 'limit' is not present"} // {"message":"Required Long parameter 'from' is not present"} // {"message":"Required Long parameter 'to' is not present"} // {"message":"Invalid status. status=6 not support any more, please use 3:deal_success orders, 4:cancelled orders"} // {"message":"Not found"} // {"message":"Place order error"} // const feedback = this.id + ' ' + body; const message = this.safeString2 (response, 'message', 'msg'); if (message !== undefined) { this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],38:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ArgumentsRequired, AuthenticationError, ExchangeError, InsufficientFunds, InvalidOrder, BadSymbol } = require ('./base/errors'); const { ROUND } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class bitmax extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitmax', 'name': 'BitMax', 'countries': [ 'CN' ], // China 'rateLimit': 500, 'certified': false, // new metainfo interface 'has': { 'CORS': false, 'fetchAccounts': true, 'fetchTickers': true, 'fetchOHLCV': true, 'fetchMyTrades': false, 'fetchOrder': true, 'fetchOrders': false, 'fetchOpenOrders': true, 'fetchOrderTrades': true, 'fetchClosedOrders': true, 'fetchTransactions': false, 'fetchCurrencies': true, 'cancelAllOrders': true, 'fetchDepositAddress': true, }, 'timeframes': { '1m': '1', '3m': '3', '5m': '5', '15m': '15', '30m': '30', '1h': '60', '2h': '120', '4h': '240', '6h': '360', '12h': '720', '1d': '1d', '1w': '1w', '1M': '1m', }, 'version': 'v1', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/66820319-19710880-ef49-11e9-8fbe-16be62a11992.jpg', 'api': 'https://bitmax.io', 'test': 'https://bitmax-test.io', 'www': 'https://bitmax.io', 'doc': [ 'https://github.com/bitmax-exchange/api-doc/blob/master/bitmax-api-doc-v1.2.md', ], 'fees': 'https://bitmax.io/#/feeRate/tradeRate', 'referral': 'https://bitmax.io/#/register?inviteCode=EL6BXBQM', }, 'api': { 'public': { 'get': [ 'assets', 'depth', 'fees', 'quote', 'depth', 'trades', 'products', 'ticker/24hr', 'barhist', 'barhist/info', 'margin/ref-price', ], }, 'private': { 'get': [ 'deposit', 'user/info', 'balance', 'order/batch', 'order/open', 'order', 'order/history', 'order/{coid}', 'order/fills/{coid}', 'transaction', 'margin/balance', 'margin/order/open', 'margin/order', ], 'post': [ 'margin/order', 'order', 'order/batch', ], 'delete': [ 'margin/order', 'order', 'order/all', 'order/batch', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.001, 'maker': 0.001, }, }, 'options': { 'accountGroup': undefined, 'parseOrderToPrecision': false, }, 'exceptions': { 'exact': { '2100': AuthenticationError, // {"code":2100,"message":"ApiKeyFailure"} '5002': BadSymbol, // {"code":5002,"message":"Invalid Symbol"} '6001': BadSymbol, // {"code":6001,"message":"Trading is disabled on symbol."} '6010': InsufficientFunds, // {'code': 6010, 'message': 'Not enough balance.'} '60060': InvalidOrder, // { 'code': 60060, 'message': 'The order is already filled or canceled.' } '600503': InvalidOrder, // {"code":600503,"message":"Notional is too small."} }, 'broad': {}, }, 'commonCurrencies': { 'BTCBEAR': 'BEAR', 'BTCBULL': 'BULL', }, }); } async fetchCurrencies (params = {}) { const response = await this.publicGetAssets (params); // // [ // { // "assetCode" : "LTO", // "assetName" : "LTO", // "precisionScale" : 9, // "nativeScale" : 3, // "withdrawalFee" : 5.0, // "minWithdrawalAmt" : 10.0, // "status" : "Normal" // }, // ] // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'assetCode'); // todo: will need to rethink the fees // to add support for multiple withdrawal/deposit methods and // differentiated fees for each particular method const code = this.safeCurrencyCode (id); const precision = this.safeInteger (currency, 'precisionScale'); const fee = this.safeFloat (currency, 'withdrawalFee'); // todo: redesign const status = this.safeString (currency, 'status'); const active = (status === 'Normal'); result[code] = { 'id': id, 'code': code, 'info': currency, 'type': undefined, 'name': this.safeString (currency, 'assetName'), 'active': active, 'fee': fee, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'minWithdrawalAmt'), 'max': undefined, }, }, }; } return result; } async fetchMarkets (params = {}) { const response = await this.publicGetProducts (params); // // [ // { // "symbol" : "BCH/USDT", // "domain" : "USDS", // "baseAsset" : "BCH", // "quoteAsset" : "USDT", // "priceScale" : 2, // "qtyScale" : 3, // "notionalScale" : 9, // "minQty" : "0.000000001", // "maxQty" : "1000000000", // "minNotional" : "5", // "maxNotional" : "200000", // "status" : "Normal", // "miningStatus" : "", // "marginTradable" : true, // "commissionType" : "Quote", // "commissionReserveRate" : 0.0010000000 // }, // ] // const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'symbol'); const baseId = this.safeString (market, 'baseAsset'); const quoteId = this.safeString (market, 'quoteAsset'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'qtyScale'), 'price': this.safeInteger (market, 'notionalScale'), }; const status = this.safeString (market, 'status'); const active = (status === 'Normal'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minQty'), 'max': this.safeFloat (market, 'maxQty'), }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': this.safeFloat (market, 'minNotional'), 'max': this.safeFloat (market, 'maxNotional'), }, }, }); } return result; } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; let key = 'quote'; const rate = market[takerOrMaker]; let cost = amount * rate; let precision = market['precision']['price']; if (side === 'sell') { cost *= price; } else { key = 'base'; precision = market['precision']['amount']; } cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': parseFloat (cost), }; } async fetchAccounts (params = {}) { let accountGroup = this.safeString (this.options, 'accountGroup'); let response = undefined; if (accountGroup === undefined) { response = await this.privateGetUserInfo (params); // // { // "accountGroup": 5 // } // accountGroup = this.safeString (response, 'accountGroup'); } return [ { 'id': accountGroup, 'type': undefined, 'currency': undefined, 'info': response, }, ]; } async fetchBalance (params = {}) { await this.loadMarkets (); await this.loadAccounts (); const response = await this.privateGetBalance (params); // // { // "code": 0, // "status": "success", // this field will be deprecated soon // "email": "foo@bar.com", // this field will be deprecated soon // "data": [ // { // "assetCode": "TSC", // "assetName": "Ethereum", // "totalAmount": "20.03", // total balance amount // "availableAmount": "20.03", // balance amount available to trade // "inOrderAmount": "0.000", // in order amount // "btcValue": "70.81" // the current BTC value of the balance, may be missing // }, // ] // } // const result = { 'info': response }; const balances = this.safeValue (response, 'data', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const code = this.safeCurrencyCode (this.safeString (balance, 'assetCode')); const account = this.account (); account['free'] = this.safeFloat (balance, 'availableAmount'); account['used'] = this.safeFloat (balance, 'inOrderAmount'); account['total'] = this.safeFloat (balance, 'totalAmount'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['n'] = limit; // default = maximum = 100 } const response = await this.publicGetDepth (this.extend (request, params)); // // { // "m":"depth", // "ts":1570866464777, // "seqnum":5124140078, // "s":"ETH/USDT", // "asks":[ // ["183.57","5.92"], // ["183.6","10.185"] // ], // "bids":[ // ["183.54","0.16"], // ["183.53","10.8"], // ] // } // const timestamp = this.safeInteger (response, 'ts'); const result = this.parseOrderBook (response, timestamp); result['nonce'] = this.safeInteger (response, 'seqnum'); return result; } parseTicker (ticker, market = undefined) { // // { // "symbol":"BCH/USDT", // "interval":"1d", // "barStartTime":1570866600000, // "openPrice":"225.16", // "closePrice":"224.05", // "highPrice":"226.08", // "lowPrice":"218.92", // "volume":"8607.036" // } // const timestamp = this.safeInteger (ticker, 'barStartTime'); let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else if (marketId !== undefined) { const [ baseId, quoteId ] = marketId.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'closePrice'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'highPrice'), 'low': this.safeFloat (ticker, 'lowPrice'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'openPrice'), 'close': last, 'last': last, 'previousClose': undefined, // previous day close 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } parseTickers (rawTickers, symbols = undefined) { const tickers = []; for (let i = 0; i < rawTickers.length; i++) { tickers.push (this.parseTicker (rawTickers[i])); } return this.filterByArray (tickers, 'symbol', symbols); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTicker24hr (this.extend (request, params)); return this.parseTicker (response, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker24hr (params); return this.parseTickers (response, symbols); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // { // "m":"bar", // "s":"ETH/BTC", // "ba":"ETH", // "qa":"BTC", // "i":"1", // "t":1570867020000, // "o":"0.022023", // "c":"0.022018", // "h":"0.022023", // "l":"0.022018", // "v":"2.510", // } // return [ this.safeInteger (ohlcv, 't'), this.safeFloat (ohlcv, 'o'), this.safeFloat (ohlcv, 'h'), this.safeFloat (ohlcv, 'l'), this.safeFloat (ohlcv, 'c'), this.safeFloat (ohlcv, 'v'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'interval': this.timeframes[timeframe], }; // if since and limit are not specified // the exchange will return just 1 last candle by default const duration = this.parseTimeframe (timeframe); if (since !== undefined) { request['from'] = since; if (limit !== undefined) { request['to'] = this.sum (since, limit * duration * 1000, 1); } } else if (limit !== undefined) { request['to'] = this.milliseconds (); request['from'] = request['to'] - limit * duration * 1000 - 1; } const response = await this.publicGetBarhist (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // public fetchTrades // // { // "p": "13.75", // price // "q": "6.68", // quantity // "t": 1528988084944, // timestamp // "bm": False, // if true, the buyer is the market maker, we only use this field to "define the side" of a public trade // } // // private fetchOrderTrades // // { // "ap": "0.029062965", // average filled price // "bb": "36851.981", // base asset total balance // "bc": "0", // if possitive, this is the BTMX commission charged by reverse mining, if negative, this is the mining output of the current fill. // "bpb": "36851.981", // base asset pending balance // "btmxBal": "0.0", // optional, the BTMX balance of the current account. This field is only available when bc is non-zero. // "cat": "CASH", // account category: CASH/MARGIN // "coid": "41g6wtPRFrJXgg6YxjqI6Qoog139Dmoi", // client order id, (needed to cancel order) // "ei": "NULL_VAL", // execution instruction // "errorCode": "NULL_VAL", // if the order is rejected, this field explains why // "execId": "12562285", // for each user, this is a strictly increasing long integer (represented as string) // "f": "78.074", // filled quantity, this is the aggregated quantity executed by all past fills // "fa": "BTC", // fee asset // "fee": "0.000693608", // fee // 'lp': "0.029064", // last price, the price executed by the last fill // "l": "11.932", // last quantity, the quantity executed by the last fill // "m": "order", // message type // "orderType": "Limit", // Limit, Market, StopLimit, StopMarket // "p": "0.029066", // limit price, only available for limit and stop limit orders // "q": "100.000", // order quantity // "qb": "98878.642957097", // quote asset total balance // "qpb": "98877.967247508", // quote asset pending balance // "s": "ETH/BTC", // symbol // "side": "Buy", // side // "status": "PartiallyFilled", // order status // "t": 1561131458389, // timestamp // } // const timestamp = this.safeInteger (trade, 't'); const price = this.safeFloat (trade, 'p'); const amount = this.safeFloat (trade, 'q'); let cost = undefined; if ((price !== undefined) && (amount !== undefined)) { cost = price * amount; } const buyerIsMaker = this.safeValue (trade, 'bm'); let symbol = undefined; const marketId = this.safeString (trade, 's'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = market.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'fa'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } const orderId = this.safeString (trade, 'coid'); let side = this.safeStringLower (trade, 'side'); if ((side === undefined) && (buyerIsMaker !== undefined)) { side = buyerIsMaker ? 'buy' : 'sell'; } const type = this.safeStringLower (trade, 'orderType'); return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': undefined, 'order': orderId, 'type': type, 'takerOrMaker': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['n'] = limit; // currently limited to 100 or fewer } const response = await this.publicGetTrades (this.extend (request, params)); // // { // "m": "marketTrades", // message type // "s": "ETH/BTC", // symbol // "trades": [ // { // "p": "13.75", // price // "q": "6.68", // quantity // "t": 1528988084944, // timestamp // "bm": False, // if true, the buyer is the market maker // }, // ] // } // const trades = this.safeValue (response, 'trades', []); return this.parseTrades (trades, market, since, limit); } parseOrderStatus (status) { const statuses = { 'PendingNew': 'open', 'New': 'open', 'PartiallyFilled': 'open', 'Filled': 'closed', 'Canceled': 'canceled', 'Rejected': 'rejected', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "coid": "xxx...xxx", // "action": "new", // "success": true // success = true means the order has been submitted to the matching engine. // } // // fetchOrder, fetchOpenOrders, fetchClosedOrders // // { // "accountCategory": "CASH", // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", // "avgPrice": "0.000000000", // "baseAsset": "ETH", // "btmxCommission": "0.000000000", // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order // "errorCode": "NULL_VAL", // "execId": "12452288", // "execInst": "NULL_VAL", // "fee": "0.000000000", // cumulative fee paid for this order // "feeAsset": "", // the asset // "filledQty": "0.000000000", // filled quantity // "notional": "0.000000000", // "orderPrice": "0.310000000", // only available for limit and stop limit orders // "orderQty": "1.000000000", // "orderType": "StopLimit", // "quoteAsset": "BTC", // "side": "Buy", // "status": "PendingNew", // "stopPrice": "0.300000000", // only available for stop market and stop limit orders // "symbol": "ETH/BTC", // "time": 1566091628227, // The last execution time of the order // "sendingTime": 1566091503547, // The sending time of the order // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" // } // const status = this.parseOrderStatus (this.safeString (order, 'status')); const marketId = this.safeString (order, 'symbol'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.safeInteger (order, 'sendingTime'); let price = this.safeFloat (order, 'orderPrice'); const amount = this.safeFloat (order, 'orderQty'); const filled = this.safeFloat (order, 'filledQty'); let remaining = undefined; let cost = this.safeFloat (order, 'cummulativeQuoteQty'); if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; if (this.options['parseOrderToPrecision']) { remaining = parseFloat (this.amountToPrecision (symbol, remaining)); } remaining = Math.max (remaining, 0.0); } if (price !== undefined) { if (cost === undefined) { cost = price * filled; } } } const id = this.safeString (order, 'coid'); let type = this.safeString (order, 'orderType'); if (type !== undefined) { type = type.toLowerCase (); if (type === 'market') { if (price === 0.0) { if ((cost !== undefined) && (filled !== undefined)) { if ((cost > 0) && (filled > 0)) { price = cost / filled; } } } } } const side = this.safeStringLower (order, 'side'); const fee = { 'cost': this.safeFloat (order, 'fee'), 'currency': this.safeString (order, 'feeAsset'), }; const average = this.safeFloat (order, 'avgPrice'); const clientOrderId = id; return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); await this.loadAccounts (); const market = this.market (symbol); const request = { 'coid': this.coid (), // a unique identifier of length 32 // 'time': this.milliseconds (), // milliseconds since UNIX epoch in UTC, this is filled in the private section of the sign() method below 'symbol': market['id'], // 'orderPrice': this.priceToPrecision (symbol, price), // optional, limit price of the order. This field is required for limit orders and stop limit orders // 'stopPrice': '15.7', // optional, stopPrice of the order. This field is required for stop_market orders and stop limit orders 'orderQty': this.amountToPrecision (symbol, amount), 'orderType': type, // order type, you shall specify one of the following: "limit", "market", "stop_market", "stop_limit" 'side': side, // "buy" or "sell" // 'postOnly': true, // optional, if true, the order will either be posted to the limit order book or be cancelled, i.e. the order cannot take liquidity, default is false // 'timeInForce': 'GTC', // optional, supports "GTC" good-till-canceled and "IOC" immediate-or-cancel }; if ((type === 'limit') || (type === 'stop_limit')) { request['orderPrice'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostOrder (this.extend (request, params)); // // { // "code": 0, // "email": "foo@bar.com", // this field will be deprecated soon // "status": "success", // this field will be deprecated soon // "data": { // "coid": "xxx...xxx", // "action": "new", // "success": true, // success = true means the order has been submitted to the matching engine // } // } // const data = this.safeValue (response, 'data', {}); return this.parseOrder (data, market); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); await this.loadAccounts (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'coid': id, }; const response = await this.privateGetOrderCoid (this.extend (request, params)); // // { // 'code': 0, // 'status': 'success', // this field will be deprecated soon // 'email': 'foo@bar.com', // this field will be deprecated soon // "data": { // "accountCategory": "CASH", // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", // "avgPrice": "0.000000000", // "baseAsset": "ETH", // "btmxCommission": "0.000000000", // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order // "errorCode": "NULL_VAL", // "execId": "12452288", // "execInst": "NULL_VAL", // "fee": "0.000000000", // cumulative fee paid for this order // "feeAsset": "", // the asset // "filledQty": "0.000000000", // filled quantity // "notional": "0.000000000", // "orderPrice": "0.310000000", // only available for limit and stop limit orders // "orderQty": "1.000000000", // "orderType": "StopLimit", // "quoteAsset": "BTC", // "side": "Buy", // "status": "PendingNew", // "stopPrice": "0.300000000", // only available for stop market and stop limit orders // "symbol": "ETH/BTC", // "time": 1566091628227, // The last execution time of the order // "sendingTime": 1566091503547, // The sending time of the order // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" // } // } // const data = this.safeValue (response, 'data', {}); return this.parseOrder (data, market); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); await this.loadAccounts (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'coid': id, }; const response = await this.privateGetOrderFillsCoid (this.extend (request, params)); // // { // 'code': 0, // 'status': 'success', // this field will be deprecated soon // 'email': 'foo@bar.com', // this field will be deprecated soon // "data": [ // { // "ap": "0.029062965", // average filled price // "bb": "36851.981", // base asset total balance // "bc": "0", // if possitive, this is the BTMX commission charged by reverse mining, if negative, this is the mining output of the current fill. // "bpb": "36851.981", // base asset pending balance // "btmxBal": "0.0", // optional, the BTMX balance of the current account. This field is only available when bc is non-zero. // "cat": "CASH", // account category: CASH/MARGIN // "coid": "41g6wtPRFrJXgg6YxjqI6Qoog139Dmoi", // client order id, (needed to cancel order) // "ei": "NULL_VAL", // execution instruction // "errorCode": "NULL_VAL", // if the order is rejected, this field explains why // "execId": "12562285", // for each user, this is a strictly increasing long integer (represented as string) // "f": "78.074", // filled quantity, this is the aggregated quantity executed by all past fills // "fa": "BTC", // fee asset // "fee": "0.000693608", // fee // 'lp': "0.029064", // last price, the price executed by the last fill // "l": "11.932", // last quantity, the quantity executed by the last fill // "m": "order", // message type // "orderType": "Limit", // Limit, Market, StopLimit, StopMarket // "p": "0.029066", // limit price, only available for limit and stop limit orders // "q": "100.000", // order quantity // "qb": "98878.642957097", // quote asset total balance // "qpb": "98877.967247508", // quote asset pending balance // "s": "ETH/BTC", // symbol // "side": "Buy", // side // "status": "PartiallyFilled", // order status // "t": 1561131458389, // timestamp // }, // ] // } // const data = this.safeValue (response, 'data', {}); return this.parseTrades (data, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); await this.loadAccounts (); let market = undefined; const request = { // 'side': 'buy', // or 'sell', optional }; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } const response = await this.privateGetOrderOpen (this.extend (request, params)); // // { // 'code': 0, // 'status': 'success', // this field will be deprecated soon // 'email': 'foo@bar.com', // this field will be deprecated soon // "data": [ // { // "accountCategory": "CASH", // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", // "avgPrice": "0.000000000", // "baseAsset": "ETH", // "btmxCommission": "0.000000000", // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order // "errorCode": "NULL_VAL", // "execId": "12452288", // "execInst": "NULL_VAL", // "fee": "0.000000000", // cumulative fee paid for this order // "feeAsset": "", // the asset // "filledQty": "0.000000000", // filled quantity // "notional": "0.000000000", // "orderPrice": "0.310000000", // only available for limit and stop limit orders // "orderQty": "1.000000000", // "orderType": "StopLimit", // "quoteAsset": "BTC", // "side": "Buy", // "status": "PendingNew", // "stopPrice": "0.300000000", // only available for stop market and stop limit orders // "symbol": "ETH/BTC", // "time": 1566091628227, // The last execution time of the order // "sendingTime": 1566091503547, // The sending time of the order // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" // }, // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOrders (data, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); await this.loadAccounts (); let market = undefined; const request = { // 'symbol': 'ETH/BTC', // optional // 'category': 'CASH', // optional, string // 'orderType': 'Market', // optional, string // 'page': 1, // optional, integer type, starts at 1 // 'pageSize': 100, // optional, integer type // 'side': 'buy', // or 'sell', optional, case insensitive. // 'startTime': 1566091628227, // optional, integer milliseconds since UNIX epoch representing the start of the range // 'endTime': 1566091628227, // optional, integer milliseconds since UNIX epoch representing the end of the range // 'status': 'Filled', // optional, can only be one of "Filled", "Canceled", "Rejected" }; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['n'] = limit; // default 15, max 50 } const response = await this.privateGetOrderHistory (this.extend (request, params)); // // { // 'code': 0, // 'status': 'success', // this field will be deprecated soon // 'email': 'foo@bar.com', // this field will be deprecated soon // 'data': { // 'page': 1, // 'pageSize': 20, // 'limit': 500, // 'hasNext': False, // 'data': [ // { // 'time': 1566091429000, // The last execution time of the order (This timestamp is in second level resolution) // 'coid': 'QgQIMJhPFrYfUf60ZTihmseTqhzzwOCx', // 'execId': '331', // 'symbol': 'BTMX/USDT', // 'orderType': 'Market', // 'baseAsset': 'BTMX', // 'quoteAsset': 'USDT', // 'side': 'Buy', // 'stopPrice': '0.000000000', // only meaningful for stop market and stop limit orders // 'orderPrice': '0.123000000', // only meaningful for limit and stop limit orders // 'orderQty': '9229.409000000', // 'filledQty': '9229.409000000', // 'avgPrice': '0.095500000', // 'fee': '0.352563424', // 'feeAsset': 'USDT', // 'btmxCommission': '0.000000000', // 'status': 'Filled', // 'notional': '881.408559500', // 'userId': '5DNEppWy33SayHjFQpgQUTjwNMSjEhD3', // 'accountId': 'ACPHERRWRIA3VQADMEAB2ZTLYAXNM3PJ', // 'accountCategory': 'CASH', // 'errorCode': 'NULL_VAL', // 'execInst': 'NULL_VAL', // "sendingTime": 1566091382736, // The sending time of the order // }, // ] // } // } // const data = this.safeValue (response, 'data', {}); const orders = this.safeValue (data, 'data', []); return this.parseOrders (orders, market, since, limit); } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); await this.loadAccounts (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'coid': this.coid (), 'origCoid': id, // 'time': this.milliseconds (), // this is filled in the private section of the sign() method below }; const response = await this.privateDeleteOrder (this.extend (request, params)); // // { // 'code': 0, // 'status': 'success', // this field will be deprecated soon // 'email': 'foo@bar.com', // this field will be deprecated soon // 'data': { // 'action': 'cancel', // 'coid': 'gaSRTi3o3Yo4PaXpVK0NSLP47vmJuLea', // 'success': True, // } // } // const order = this.safeValue (response, 'data', {}); return this.parseOrder (order); } async cancelAllOrders (symbol = undefined, params = {}) { await this.loadMarkets (); await this.loadAccounts (); const request = { // 'side': 'buy', // optional string field (case-insensitive), either "buy" or "sell" }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; // optional } const response = await this.privateDeleteOrderAll (this.extend (request, params)); // // ? // return response; } coid () { const uuid = this.uuid (); const parts = uuid.split ('-'); const clientOrderId = parts.join (''); const coid = clientOrderId.slice (0, 32); return coid; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); await this.loadAccounts (); const currency = this.currency (code); const request = { 'requestId': this.coid (), // 'time': this.milliseconds (), // this is filled in the private section of the sign() method below 'assetCode': currency['id'], }; // note: it is highly recommended to use V2 version of this route, // especially for assets with multiple block chains such as USDT. const response = await this.privateGetDeposit (this.extend (request, params)); // // v1 // // { // "data": { // "address": "0x26a3CB49578F07000575405a57888681249c35Fd" // }, // "email": "igor.kroitor@gmial.com", // "status": "success", // } // // v2 // // { // "code": 0, // "data": [ // { // "asset": "XRP", // "blockChain": "Ripple", // "addressData": { // "address": "rpinhtY4p35bPmVXPbfWRUtZ1w1K1gYShB", // "destTag": "54301" // } // } // ], // "email": "xxx@xxx.com", // "status": "success" // the request has been submitted to the server // } // let addressData = this.safeValue (response, 'data'); if (Array.isArray (addressData)) { const firstElement = this.safeValue (addressData, 0, {}); addressData = this.safeValue (firstElement, 'addressData', {}); } const address = this.safeString (addressData, 'address'); const tag = this.safeString (addressData, 'destTag'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = '/api/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); let accountGroup = this.safeString (this.options, 'accountGroup'); if (accountGroup === undefined) { if (this.accounts !== undefined) { accountGroup = this.accounts[0]['id']; } } if (accountGroup !== undefined) { url = '/' + accountGroup + url; } const coid = this.safeString (query, 'coid'); query['time'] = this.milliseconds ().toString (); let auth = query['time'] + '+' + path.replace ('/{coid}', ''); // fix sign error headers = { 'x-auth-key': this.apiKey, 'x-auth-timestamp': query['time'], 'Content-Type': 'application/json', }; if (coid !== undefined) { auth += '+' + coid; headers['x-auth-coid'] = coid; } const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); headers['x-auth-signature'] = this.decode (signature); if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { body = this.json (query); } } url = this.urls['api'] + url; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // // {"code":2100,"message":"ApiKeyFailure"} // {'code': 6010, 'message': 'Not enough balance.'} // {'code': 60060, 'message': 'The order is already filled or canceled.'} // const code = this.safeString (response, 'code'); const message = this.safeString (response, 'message'); const error = (code !== undefined) && (code !== '0'); if (error || (message !== undefined)) { const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],39:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { TICK_SIZE } = require ('./base/functions/number'); const { AuthenticationError, BadRequest, DDoSProtection, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitmex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitmex', 'name': 'BitMEX', 'countries': [ 'SC' ], // Seychelles 'version': 'v1', 'userAgent': undefined, 'rateLimit': 2000, 'pro': true, 'has': { 'CORS': false, 'fetchOHLCV': true, 'withdraw': true, 'editOrder': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchLedger': true, 'fetchTransactions': 'emulated', }, 'timeframes': { '1m': '1m', '5m': '5m', '1h': '1h', '1d': '1d', }, 'urls': { 'test': { 'public': 'https://testnet.bitmex.com', 'private': 'https://testnet.bitmex.com', }, 'logo': 'https://user-images.githubusercontent.com/1294454/27766319-f653c6e6-5ed4-11e7-933d-f0bc3699ae8f.jpg', 'api': { 'public': 'https://www.bitmex.com', 'private': 'https://www.bitmex.com', }, 'www': 'https://www.bitmex.com', 'doc': [ 'https://www.bitmex.com/app/apiOverview', 'https://github.com/BitMEX/api-connectors/tree/master/official-http', ], 'fees': 'https://www.bitmex.com/app/fees', 'referral': 'https://www.bitmex.com/register/upZpOX', }, 'api': { 'public': { 'get': [ 'announcement', 'announcement/urgent', 'funding', 'instrument', 'instrument/active', 'instrument/activeAndIndices', 'instrument/activeIntervals', 'instrument/compositeIndex', 'instrument/indices', 'insurance', 'leaderboard', 'liquidation', 'orderBook', 'orderBook/L2', 'quote', 'quote/bucketed', 'schema', 'schema/websocketHelp', 'settlement', 'stats', 'stats/history', 'trade', 'trade/bucketed', ], }, 'private': { 'get': [ 'apiKey', 'chat', 'chat/channels', 'chat/connected', 'execution', 'execution/tradeHistory', 'notification', 'order', 'position', 'user', 'user/affiliateStatus', 'user/checkReferralCode', 'user/commission', 'user/depositAddress', 'user/margin', 'user/minWithdrawalFee', 'user/wallet', 'user/walletHistory', 'user/walletSummary', ], 'post': [ 'apiKey', 'apiKey/disable', 'apiKey/enable', 'chat', 'order', 'order/bulk', 'order/cancelAllAfter', 'order/closePosition', 'position/isolate', 'position/leverage', 'position/riskLimit', 'position/transferMargin', 'user/cancelWithdrawal', 'user/confirmEmail', 'user/confirmEnableTFA', 'user/confirmWithdrawal', 'user/disableTFA', 'user/logout', 'user/logoutAll', 'user/preferences', 'user/requestEnableTFA', 'user/requestWithdrawal', ], 'put': [ 'order', 'order/bulk', 'user', ], 'delete': [ 'apiKey', 'order', 'order/all', ], }, }, 'exceptions': { 'exact': { 'Invalid API Key.': AuthenticationError, 'This key is disabled.': PermissionDenied, 'Access Denied': PermissionDenied, 'Duplicate clOrdID': InvalidOrder, 'orderQty is invalid': InvalidOrder, 'Invalid price': InvalidOrder, 'Invalid stopPx for ordType': InvalidOrder, }, 'broad': { 'Signature not valid': AuthenticationError, 'overloaded': ExchangeNotAvailable, 'Account has insufficient Available Balance': InsufficientFunds, 'Service unavailable': ExchangeNotAvailable, // {"error":{"message":"Service unavailable","name":"HTTPError"}} }, }, 'precisionMode': TICK_SIZE, 'options': { // https://blog.bitmex.com/api_announcement/deprecation-of-api-nonce-header/ // https://github.com/ccxt/ccxt/issues/4789 'api-expires': 5, // in seconds 'fetchOHLCVOpenTimestamp': true, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetInstrumentActiveAndIndices (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const active = (market['state'] !== 'Unlisted'); const id = market['symbol']; const baseId = market['underlying']; const quoteId = market['quoteCurrency']; const basequote = baseId + quoteId; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const swap = (id === basequote); // 'positionCurrency' may be empty ("", as Bitmex currently returns for ETHUSD) // so let's take the quote currency first and then adjust if needed const positionId = this.safeString2 (market, 'positionCurrency', 'quoteCurrency'); let type = undefined; let future = false; let prediction = false; const position = this.safeCurrencyCode (positionId); let symbol = id; if (swap) { type = 'swap'; symbol = base + '/' + quote; } else if (id.indexOf ('B_') >= 0) { prediction = true; type = 'prediction'; } else { future = true; type = 'future'; } const precision = { 'amount': undefined, 'price': undefined, }; const lotSize = this.safeFloat (market, 'lotSize'); const tickSize = this.safeFloat (market, 'tickSize'); if (lotSize !== undefined) { precision['amount'] = lotSize; } if (tickSize !== undefined) { precision['price'] = tickSize; } const limits = { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': tickSize, 'max': this.safeFloat (market, 'maxPrice'), }, 'cost': { 'min': undefined, 'max': undefined, }, }; const limitField = (position === quote) ? 'cost' : 'amount'; limits[limitField] = { 'min': lotSize, 'max': this.safeFloat (market, 'maxOrderQty'), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': limits, 'taker': this.safeFloat (market, 'takerFee'), 'maker': this.safeFloat (market, 'makerFee'), 'type': type, 'spot': false, 'swap': swap, 'future': future, 'prediction': prediction, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const request = { 'currency': 'all', }; const response = await this.privateGetUserMargin (this.extend (request, params)); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'availableMargin'); account['total'] = this.safeFloat (balance, 'marginBalance'); if (code === 'BTC') { account['free'] = account['free'] * 0.00000001; account['total'] = account['total'] * 0.00000001; } result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['depth'] = limit; } const response = await this.publicGetOrderBookL2 (this.extend (request, params)); const result = { 'bids': [], 'asks': [], 'timestamp': undefined, 'datetime': undefined, 'nonce': undefined, }; for (let i = 0; i < response.length; i++) { const order = response[i]; const side = (order['side'] === 'Sell') ? 'asks' : 'bids'; const amount = this.safeFloat (order, 'size'); const price = this.safeFloat (order, 'price'); // https://github.com/ccxt/ccxt/issues/4926 // https://github.com/ccxt/ccxt/issues/4927 // the exchange sometimes returns null price in the orderbook if (price !== undefined) { result[side].push ([ price, amount ]); } } result['bids'] = this.sortBy (result['bids'], 0, true); result['asks'] = this.sortBy (result['asks'], 0); return result; } async fetchOrder (id, symbol = undefined, params = {}) { const filter = { 'filter': { 'orderID': id, }, }; const response = await this.fetchOrders (symbol, undefined, undefined, this.deepExtend (filter, params)); const numResults = response.length; if (numResults === 1) { return response[0]; } throw new OrderNotFound (this.id + ': The order ' + id + ' not found.'); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; let request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['startTime'] = this.iso8601 (since); } if (limit !== undefined) { request['count'] = limit; } request = this.deepExtend (request, params); // why the hassle? urlencode in python is kinda broken for nested dicts. // E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}" // Bitmex doesn't like that. Hence resorting to this hack. if ('filter' in request) { request['filter'] = this.json (request['filter']); } const response = await this.privateGetOrder (request); return this.parseOrders (response, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'filter': { 'open': true, }, }; return await this.fetchOrders (symbol, since, limit, this.deepExtend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { // Bitmex barfs if you set 'open': false in the filter... const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'closed'); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; let request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['startTime'] = this.iso8601 (since); } if (limit !== undefined) { request['count'] = limit; } request = this.deepExtend (request, params); // why the hassle? urlencode in python is kinda broken for nested dicts. // E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}" // Bitmex doesn't like that. Hence resorting to this hack. if ('filter' in request) { request['filter'] = this.json (request['filter']); } const response = await this.privateGetExecutionTradeHistory (request); // // [ // { // "execID": "string", // "orderID": "string", // "clOrdID": "string", // "clOrdLinkID": "string", // "account": 0, // "symbol": "string", // "side": "string", // "lastQty": 0, // "lastPx": 0, // "underlyingLastPx": 0, // "lastMkt": "string", // "lastLiquidityInd": "string", // "simpleOrderQty": 0, // "orderQty": 0, // "price": 0, // "displayQty": 0, // "stopPx": 0, // "pegOffsetValue": 0, // "pegPriceType": "string", // "currency": "string", // "settlCurrency": "string", // "execType": "string", // "ordType": "string", // "timeInForce": "string", // "execInst": "string", // "contingencyType": "string", // "exDestination": "string", // "ordStatus": "string", // "triggered": "string", // "workingIndicator": true, // "ordRejReason": "string", // "simpleLeavesQty": 0, // "leavesQty": 0, // "simpleCumQty": 0, // "cumQty": 0, // "avgPx": 0, // "commission": 0, // "tradePublishIndicator": "string", // "multiLegReportingType": "string", // "text": "string", // "trdMatchID": "string", // "execCost": 0, // "execComm": 0, // "homeNotional": 0, // "foreignNotional": 0, // "transactTime": "2019-03-05T12:47:02.762Z", // "timestamp": "2019-03-05T12:47:02.762Z" // } // ] // return this.parseTrades (response, market, since, limit); } parseLedgerEntryType (type) { const types = { 'Withdrawal': 'transaction', 'RealisedPNL': 'margin', 'UnrealisedPNL': 'margin', 'Deposit': 'transaction', 'Transfer': 'transfer', 'AffiliatePayout': 'referral', }; return this.safeString (types, type, type); } parseLedgerEntry (item, currency = undefined) { // // { // transactID: "69573da3-7744-5467-3207-89fd6efe7a47", // account: 24321, // currency: "XBt", // transactType: "Withdrawal", // "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ... // amount: -1000000, // fee: 300000, // transactStatus: "Completed", // "Canceled", ... // address: "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9", // tx: "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA", // text: "", // transactTime: "2017-03-21T20:05:14.388Z", // walletBalance: 0, // balance after // marginBalance: null, // timestamp: "2017-03-22T13:09:23.514Z" // } // // ButMEX returns the unrealized pnl from the wallet history endpoint. // The unrealized pnl transaction has an empty timestamp. // It is not related to historical pnl it has status set to "Pending". // Therefore it's not a part of the history at all. // https://github.com/ccxt/ccxt/issues/6047 // // { // "transactID":"00000000-0000-0000-0000-000000000000", // "account":121210, // "currency":"XBt", // "transactType":"UnrealisedPNL", // "amount":-5508, // "fee":0, // "transactStatus":"Pending", // "address":"XBTUSD", // "tx":"", // "text":"", // "transactTime":null, # ←---------------------------- null // "walletBalance":139198767, // "marginBalance":139193259, // "timestamp":null # ←---------------------------- null // } // const id = this.safeString (item, 'transactID'); const account = this.safeString (item, 'account'); const referenceId = this.safeString (item, 'tx'); const referenceAccount = undefined; const type = this.parseLedgerEntryType (this.safeString (item, 'transactType')); const currencyId = this.safeString (item, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); let amount = this.safeFloat (item, 'amount'); if (amount !== undefined) { amount = amount * 1e-8; } let timestamp = this.parse8601 (this.safeString (item, 'transactTime')); if (timestamp === undefined) { // https://github.com/ccxt/ccxt/issues/6047 // set the timestamp to zero, 1970 Jan 1 00:00:00 // for unrealized pnl and other transactions without a timestamp timestamp = 0; // see comments above } let feeCost = this.safeFloat (item, 'fee', 0); if (feeCost !== undefined) { feeCost = feeCost * 1e-8; } const fee = { 'cost': feeCost, 'currency': code, }; let after = this.safeFloat (item, 'walletBalance'); if (after !== undefined) { after = after * 1e-8; } const before = this.sum (after, -amount); let direction = undefined; if (amount < 0) { direction = 'out'; amount = Math.abs (amount); } else { direction = 'in'; } const status = this.parseTransactionStatus (this.safeString (item, 'transactStatus')); return { 'id': id, 'info': item, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'direction': direction, 'account': account, 'referenceId': referenceId, 'referenceAccount': referenceAccount, 'type': type, 'currency': code, 'amount': amount, 'before': before, 'after': after, 'status': status, 'fee': fee, }; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; if (code !== undefined) { currency = this.currency (code); } const request = { // 'start': 123, }; // // if (since !== undefined) { // // date-based pagination not supported // } // if (limit !== undefined) { request['count'] = limit; } const response = await this.privateGetUserWalletHistory (this.extend (request, params)); // // [ // { // transactID: "69573da3-7744-5467-3207-89fd6efe7a47", // account: 24321, // currency: "XBt", // transactType: "Withdrawal", // "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ... // amount: -1000000, // fee: 300000, // transactStatus: "Completed", // "Canceled", ... // address: "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9", // tx: "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA", // text: "", // transactTime: "2017-03-21T20:05:14.388Z", // walletBalance: 0, // balance after // marginBalance: null, // timestamp: "2017-03-22T13:09:23.514Z" // } // ] // return this.parseLedger (response, currency, since, limit); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'start': 123, }; // // if (since !== undefined) { // // date-based pagination not supported // } // if (limit !== undefined) { request['count'] = limit; } const response = await this.privateGetUserWalletHistory (this.extend (request, params)); const transactions = this.filterByArray (response, 'transactType', [ 'Withdrawal', 'Deposit' ], false); let currency = undefined; if (code !== undefined) { currency = this.currency (code); } return this.parseTransactions (transactions, currency, since, limit); } parseTransactionStatus (status) { const statuses = { 'Canceled': 'canceled', 'Completed': 'ok', 'Pending': 'pending', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // { // 'transactID': 'ffe699c2-95ee-4c13-91f9-0faf41daec25', // 'account': 123456, // 'currency': 'XBt', // 'transactType': 'Withdrawal', // 'amount': -100100000, // 'fee': 100000, // 'transactStatus': 'Completed', // 'address': '385cR5DM96n1HvBDMzLHPYcw89fZAXULJP', // 'tx': '3BMEXabcdefghijklmnopqrstuvwxyz123', // 'text': '', // 'transactTime': '2019-01-02T01:00:00.000Z', // 'walletBalance': 99900000, // 'marginBalance': None, // 'timestamp': '2019-01-02T13:00:00.000Z' // } // const id = this.safeString (transaction, 'transactID'); // For deposits, transactTime == timestamp // For withdrawals, transactTime is submission, timestamp is processed const transactTime = this.parse8601 (this.safeString (transaction, 'transactTime')); const timestamp = this.parse8601 (this.safeString (transaction, 'timestamp')); const type = this.safeStringLower (transaction, 'transactType'); // Deposits have no from address or to address, withdrawals have both let address = undefined; let addressFrom = undefined; let addressTo = undefined; if (type === 'withdrawal') { address = this.safeString (transaction, 'address'); addressFrom = this.safeString (transaction, 'tx'); addressTo = address; } let amount = this.safeInteger (transaction, 'amount'); if (amount !== undefined) { amount = Math.abs (amount) * 1e-8; } let feeCost = this.safeInteger (transaction, 'fee'); if (feeCost !== undefined) { feeCost = feeCost * 1e-8; } const fee = { 'cost': feeCost, 'currency': 'BTC', }; let status = this.safeString (transaction, 'transactStatus'); if (status !== undefined) { status = this.parseTransactionStatus (status); } return { 'info': transaction, 'id': id, 'txid': undefined, 'timestamp': transactTime, 'datetime': this.iso8601 (transactTime), 'addressFrom': addressFrom, 'address': address, 'addressTo': addressTo, 'tagFrom': undefined, 'tag': undefined, 'tagTo': undefined, 'type': type, 'amount': amount, // BTC is the only currency on Bitmex 'currency': 'BTC', 'status': status, 'updated': timestamp, 'comment': undefined, 'fee': fee, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (!market['active']) { throw new ExchangeError (this.id + ': symbol ' + symbol + ' is delisted'); } const tickers = await this.fetchTickers ([ symbol ], params); const ticker = this.safeValue (tickers, symbol); if (ticker === undefined) { throw new ExchangeError (this.id + ' ticker symbol ' + symbol + ' not found'); } return ticker; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetInstrumentActiveAndIndices (params); const result = {}; for (let i = 0; i < response.length; i++) { const ticker = this.parseTicker (response[i]); const symbol = this.safeString (ticker, 'symbol'); if (symbol !== undefined) { result[symbol] = ticker; } } return result; } parseTicker (ticker, market = undefined) { // // { symbol: "ETHH19", // rootSymbol: "ETH", // state: "Open", // typ: "FFCCSX", // listing: "2018-12-17T04:00:00.000Z", // front: "2019-02-22T12:00:00.000Z", // expiry: "2019-03-29T12:00:00.000Z", // settle: "2019-03-29T12:00:00.000Z", // relistInterval: null, // inverseLeg: "", // sellLeg: "", // buyLeg: "", // optionStrikePcnt: null, // optionStrikeRound: null, // optionStrikePrice: null, // optionMultiplier: null, // positionCurrency: "ETH", // underlying: "ETH", // quoteCurrency: "XBT", // underlyingSymbol: "ETHXBT=", // reference: "BMEX", // referenceSymbol: ".BETHXBT30M", // calcInterval: null, // publishInterval: null, // publishTime: null, // maxOrderQty: 100000000, // maxPrice: 10, // lotSize: 1, // tickSize: 0.00001, // multiplier: 100000000, // settlCurrency: "XBt", // underlyingToPositionMultiplier: 1, // underlyingToSettleMultiplier: null, // quoteToSettleMultiplier: 100000000, // isQuanto: false, // isInverse: false, // initMargin: 0.02, // maintMargin: 0.01, // riskLimit: 5000000000, // riskStep: 5000000000, // limit: null, // capped: false, // taxed: true, // deleverage: true, // makerFee: -0.0005, // takerFee: 0.0025, // settlementFee: 0, // insuranceFee: 0, // fundingBaseSymbol: "", // fundingQuoteSymbol: "", // fundingPremiumSymbol: "", // fundingTimestamp: null, // fundingInterval: null, // fundingRate: null, // indicativeFundingRate: null, // rebalanceTimestamp: null, // rebalanceInterval: null, // openingTimestamp: "2019-02-13T08:00:00.000Z", // closingTimestamp: "2019-02-13T09:00:00.000Z", // sessionInterval: "2000-01-01T01:00:00.000Z", // prevClosePrice: 0.03347, // limitDownPrice: null, // limitUpPrice: null, // bankruptLimitDownPrice: null, // bankruptLimitUpPrice: null, // prevTotalVolume: 1386531, // totalVolume: 1387062, // volume: 531, // volume24h: 17118, // prevTotalTurnover: 4741294246000, // totalTurnover: 4743103466000, // turnover: 1809220000, // turnover24h: 57919845000, // homeNotional24h: 17118, // foreignNotional24h: 579.19845, // prevPrice24h: 0.03349, // vwap: 0.03383564, // highPrice: 0.03458, // lowPrice: 0.03329, // lastPrice: 0.03406, // lastPriceProtected: 0.03406, // lastTickDirection: "ZeroMinusTick", // lastChangePcnt: 0.017, // bidPrice: 0.03406, // midPrice: 0.034065, // askPrice: 0.03407, // impactBidPrice: 0.03406, // impactMidPrice: 0.034065, // impactAskPrice: 0.03407, // hasLiquidity: true, // openInterest: 83679, // openValue: 285010674000, // fairMethod: "ImpactMidPrice", // fairBasisRate: 0, // fairBasis: 0, // fairPrice: 0.03406, // markMethod: "FairPrice", // markPrice: 0.03406, // indicativeTaxRate: 0, // indicativeSettlePrice: 0.03406, // optionUnderlyingPrice: null, // settledPrice: null, // timestamp: "2019-02-13T08:40:30.000Z", // } // let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); market = this.safeValue (this.markets_by_id, marketId, market); if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); const open = this.safeFloat (ticker, 'prevPrice24h'); const last = this.safeFloat (ticker, 'lastPrice'); let change = undefined; let percentage = undefined; if (last !== undefined && open !== undefined) { change = last - open; if (open > 0) { percentage = change / open * 100; } } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'highPrice'), 'low': this.safeFloat (ticker, 'lowPrice'), 'bid': this.safeFloat (ticker, 'bidPrice'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'askPrice'), 'askVolume': undefined, 'vwap': this.safeFloat (ticker, 'vwap'), 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': this.sum (open, last) / 2, 'baseVolume': this.safeFloat (ticker, 'homeNotional24h'), 'quoteVolume': this.safeFloat (ticker, 'foreignNotional24h'), 'info': ticker, }; } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { const timestamp = this.parse8601 (this.safeString (ohlcv, 'timestamp')); return [ timestamp, this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); // send JSON key/value pairs, such as {"key": "value"} // filter by individual fields and do advanced queries on timestamps // let filter = { 'key': 'value' }; // send a bare series (e.g. XBU) to nearest expiring contract in that series // you can also send a timeframe, e.g. XBU:monthly // timeframes: daily, weekly, monthly, quarterly, and biquarterly const market = this.market (symbol); const request = { 'symbol': market['id'], 'binSize': this.timeframes[timeframe], 'partial': true, // true == include yet-incomplete current bins // 'filter': filter, // filter by individual fields and do advanced queries // 'columns': [], // will return all columns if omitted // 'start': 0, // starting point for results (wtf?) // 'reverse': false, // true == newest first // 'endTime': '', // ending date filter for results }; if (limit !== undefined) { request['count'] = limit; // default 100, max 500 } const duration = this.parseTimeframe (timeframe) * 1000; const fetchOHLCVOpenTimestamp = this.safeValue (this.options, 'fetchOHLCVOpenTimestamp', true); // if since is not set, they will return candles starting from 2017-01-01 if (since !== undefined) { let timestamp = since; if (fetchOHLCVOpenTimestamp) { timestamp = this.sum (timestamp, duration); } const ymdhms = this.ymdhms (timestamp); request['startTime'] = ymdhms; // starting date filter for results } const response = await this.publicGetTradeBucketed (this.extend (request, params)); const result = this.parseOHLCVs (response, market, timeframe, since, limit); if (fetchOHLCVOpenTimestamp) { // bitmex returns the candle's close timestamp - https://github.com/ccxt/ccxt/issues/4446 // we can emulate the open timestamp by shifting all the timestamps one place // so the previous close becomes the current open, and we drop the first candle for (let i = 0; i < result.length; i++) { result[i][0] = result[i][0] - duration; } } return result; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // timestamp: '2018-08-28T00:00:02.735Z', // symbol: 'XBTUSD', // side: 'Buy', // size: 2000, // price: 6906.5, // tickDirection: 'PlusTick', // trdMatchID: 'b9a42432-0a46-6a2f-5ecc-c32e9ca4baf8', // grossValue: 28958000, // homeNotional: 0.28958, // foreignNotional: 2000 // } // // fetchMyTrades (private) // // { // "execID": "string", // "orderID": "string", // "clOrdID": "string", // "clOrdLinkID": "string", // "account": 0, // "symbol": "string", // "side": "string", // "lastQty": 0, // "lastPx": 0, // "underlyingLastPx": 0, // "lastMkt": "string", // "lastLiquidityInd": "string", // "simpleOrderQty": 0, // "orderQty": 0, // "price": 0, // "displayQty": 0, // "stopPx": 0, // "pegOffsetValue": 0, // "pegPriceType": "string", // "currency": "string", // "settlCurrency": "string", // "execType": "string", // "ordType": "string", // "timeInForce": "string", // "execInst": "string", // "contingencyType": "string", // "exDestination": "string", // "ordStatus": "string", // "triggered": "string", // "workingIndicator": true, // "ordRejReason": "string", // "simpleLeavesQty": 0, // "leavesQty": 0, // "simpleCumQty": 0, // "cumQty": 0, // "avgPx": 0, // "commission": 0, // "tradePublishIndicator": "string", // "multiLegReportingType": "string", // "text": "string", // "trdMatchID": "string", // "execCost": 0, // "execComm": 0, // "homeNotional": 0, // "foreignNotional": 0, // "transactTime": "2019-03-05T12:47:02.762Z", // "timestamp": "2019-03-05T12:47:02.762Z" // } // const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat2 (trade, 'size', 'lastQty'); const id = this.safeString (trade, 'trdMatchID'); const order = this.safeString (trade, 'orderID'); const side = this.safeStringLower (trade, 'side'); // price * amount doesn't work for all symbols (e.g. XBT, ETH) let cost = this.safeFloat (trade, 'execCost'); if (cost !== undefined) { cost = Math.abs (cost) / 100000000; } let fee = undefined; if ('execComm' in trade) { let feeCost = this.safeFloat (trade, 'execComm'); feeCost = feeCost / 100000000; const currencyId = this.safeString (trade, 'settlCurrency'); const feeCurrency = this.safeCurrencyCode (currencyId); const feeRate = this.safeFloat (trade, 'commission'); fee = { 'cost': feeCost, 'currency': feeCurrency, 'rate': feeRate, }; } let takerOrMaker = undefined; if (fee !== undefined) { takerOrMaker = (fee['cost'] < 0) ? 'maker' : 'taker'; } let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = marketId; } } const type = this.safeStringLower (trade, 'ordType'); return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': order, 'type': type, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'fee': fee, }; } parseOrderStatus (status) { const statuses = { 'New': 'open', 'PartiallyFilled': 'open', 'Filled': 'closed', 'DoneForDay': 'open', 'Canceled': 'canceled', 'PendingCancel': 'open', 'PendingNew': 'open', 'Rejected': 'rejected', 'Expired': 'expired', 'Stopped': 'open', 'Untriggered': 'open', 'Triggered': 'open', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const status = this.parseOrderStatus (this.safeString (order, 'ordStatus')); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } else { const marketId = this.safeString (order, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } } const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'transactTime')); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'orderQty'); const filled = this.safeFloat (order, 'cumQty', 0.0); let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = Math.max (amount - filled, 0.0); } } const average = this.safeFloat (order, 'avgPx'); let cost = undefined; if (filled !== undefined) { if (average !== undefined) { cost = average * filled; } else if (price !== undefined) { cost = price * filled; } } const id = this.safeString (order, 'orderID'); const type = this.safeStringLower (order, 'ordType'); const side = this.safeStringLower (order, 'side'); const clientOrderId = this.safeString (order, 'clOrdID'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (since !== undefined) { request['startTime'] = this.iso8601 (since); } if (limit !== undefined) { request['count'] = limit; } const response = await this.publicGetTrade (this.extend (request, params)); // // [ // { // timestamp: '2018-08-28T00:00:02.735Z', // symbol: 'XBTUSD', // side: 'Buy', // size: 2000, // price: 6906.5, // tickDirection: 'PlusTick', // trdMatchID: 'b9a42432-0a46-6a2f-5ecc-c32e9ca4baf8', // grossValue: 28958000, // homeNotional: 0.28958, // foreignNotional: 2000 // }, // { // timestamp: '2018-08-28T00:00:03.778Z', // symbol: 'XBTUSD', // side: 'Sell', // size: 1000, // price: 6906, // tickDirection: 'MinusTick', // trdMatchID: '0d4f1682-5270-a800-569b-4a0eb92db97c', // grossValue: 14480000, // homeNotional: 0.1448, // foreignNotional: 1000 // }, // ] // return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), 'side': this.capitalize (side), 'orderQty': amount, 'ordType': this.capitalize (type), }; if (price !== undefined) { request['price'] = price; } const response = await this.privatePostOrder (this.extend (request, params)); const order = this.parseOrder (response); const id = this.safeString (order, 'id'); this.orders[id] = order; return this.extend ({ 'info': response }, order); } async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderID': id, }; if (amount !== undefined) { request['orderQty'] = amount; } if (price !== undefined) { request['price'] = price; } const response = await this.privatePutOrder (this.extend (request, params)); const order = this.parseOrder (response); this.orders[order['id']] = order; return this.extend ({ 'info': response }, order); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); // https://github.com/ccxt/ccxt/issues/6507 const clOrdID = this.safeValue (params, 'clOrdID'); const request = {}; if (clOrdID === undefined) { request['orderID'] = id; } else { request['clOrdID'] = clOrdID; } const response = await this.privateDeleteOrder (this.extend (request, params)); let order = response[0]; const error = this.safeString (order, 'error'); if (error !== undefined) { if (error.indexOf ('Unable to cancel order due to existing state') >= 0) { throw new OrderNotFound (this.id + ' cancelOrder() failed: ' + error); } } order = this.parseOrder (order); this.orders[order['id']] = order; return this.extend ({ 'info': response }, order); } isFiat (currency) { if (currency === 'EUR') { return true; } if (currency === 'PLN') { return true; } return false; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); // let currency = this.currency (code); if (code !== 'BTC') { throw new ExchangeError (this.id + ' supoprts BTC withdrawals only, other currencies coming soon...'); } const request = { 'currency': 'XBt', // temporarily 'amount': amount, 'address': address, // 'otpToken': '123456', // requires if two-factor auth (OTP) is enabled // 'fee': 0.001, // bitcoin network fee }; const response = await this.privatePostUserRequestWithdrawal (this.extend (request, params)); return { 'info': response, 'id': response['transactID'], }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } if (code === 429) { throw new DDoSProtection (this.id + ' ' + body); } if (code >= 400) { const error = this.safeValue (response, 'error', {}); const message = this.safeString (error, 'message'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); if (code === 400) { throw new BadRequest (feedback); } throw new ExchangeError (feedback); // unknown message } } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let query = '/api/' + this.version + '/' + path; if (method === 'GET') { if (Object.keys (params).length) { query += '?' + this.urlencode (params); } } else { const format = this.safeString (params, '_format'); if (format !== undefined) { query += '?' + this.urlencode ({ '_format': format }); params = this.omit (params, '_format'); } } const url = this.urls['api'][api] + query; if (this.apiKey && this.secret) { let auth = method + query; let expires = this.safeInteger (this.options, 'api-expires'); headers = { 'Content-Type': 'application/json', 'api-key': this.apiKey, }; expires = this.sum (this.seconds (), expires); expires = expires.toString (); auth += expires; headers['api-expires'] = expires; if (method === 'POST' || method === 'PUT' || method === 'DELETE') { if (Object.keys (params).length) { body = this.json (params); auth += body; } } headers['api-signature'] = this.hmac (this.encode (auth), this.encode (this.secret)); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],40:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, InvalidNonce, AuthenticationError, OrderNotFound } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitso extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitso', 'name': 'Bitso', 'countries': [ 'MX' ], // Mexico 'rateLimit': 2000, // 30 requests per minute 'version': 'v3', 'has': { 'CORS': false, 'fetchMyTrades': true, 'fetchOpenOrders': true, 'fetchOrder': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766335-715ce7aa-5ed5-11e7-88a8-173a27bb30fe.jpg', 'api': 'https://api.bitso.com', 'www': 'https://bitso.com', 'doc': 'https://bitso.com/api_info', 'fees': 'https://bitso.com/fees?l=es', 'referral': 'https://bitso.com/?ref=itej', }, 'options': { 'precision': { 'XRP': 6, 'MXN': 2, 'TUSD': 2, }, 'defaultPrecision': 8, }, 'api': { 'public': { 'get': [ 'available_books', 'ticker', 'order_book', 'trades', ], }, 'private': { 'get': [ 'account_status', 'balance', 'fees', 'fundings', 'fundings/{fid}', 'funding_destination', 'kyc_documents', 'ledger', 'ledger/trades', 'ledger/fees', 'ledger/fundings', 'ledger/withdrawals', 'mx_bank_codes', 'open_orders', 'order_trades/{oid}', 'orders/{oid}', 'user_trades', 'user_trades/{tid}', 'withdrawals/', 'withdrawals/{wid}', ], 'post': [ 'bitcoin_withdrawal', 'debit_card_withdrawal', 'ether_withdrawal', 'ripple_withdrawal', 'bcash_withdrawal', 'litecoin_withdrawal', 'orders', 'phone_number', 'phone_verification', 'phone_withdrawal', 'spei_withdrawal', 'ripple_withdrawal', 'bcash_withdrawal', 'litecoin_withdrawal', ], 'delete': [ 'orders/{oid}', 'orders/all', ], }, }, 'exceptions': { '0201': AuthenticationError, // Invalid Nonce or Invalid Credentials '104': InvalidNonce, // Cannot perform request - nonce must be higher than 1520307203724237 }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetAvailableBooks (params); const markets = this.safeValue (response, 'payload'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'book'); const [ baseId, quoteId ] = id.split ('_'); let base = baseId.toUpperCase (); let quote = quoteId.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const symbol = base + '/' + quote; const limits = { 'amount': { 'min': this.safeFloat (market, 'minimum_amount'), 'max': this.safeFloat (market, 'maximum_amount'), }, 'price': { 'min': this.safeFloat (market, 'minimum_price'), 'max': this.safeFloat (market, 'maximum_price'), }, 'cost': { 'min': this.safeFloat (market, 'minimum_value'), 'max': this.safeFloat (market, 'maximum_value'), }, }; const precision = { 'amount': this.safeInteger (this.options['precision'], base, this.options['defaultPrecision']), 'price': this.safeInteger (this.options['precision'], quote, this.options['defaultPrecision']), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'limits': limits, 'precision': precision, 'active': undefined, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetBalance (params); const balances = this.safeValue (response['payload'], 'balances'); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = { 'free': this.safeFloat (balance, 'available'), 'used': this.safeFloat (balance, 'locked'), 'total': this.safeFloat (balance, 'total'), }; result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'book': this.marketId (symbol), }; const response = await this.publicGetOrderBook (this.extend (request, params)); const orderbook = this.safeValue (response, 'payload'); const timestamp = this.parse8601 (this.safeString (orderbook, 'updated_at')); return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'book': this.marketId (symbol), }; const response = await this.publicGetTicker (this.extend (request, params)); const ticker = this.safeValue (response, 'payload'); const timestamp = this.parse8601 (this.safeString (ticker, 'created_at')); const vwap = this.safeFloat (ticker, 'vwap'); const baseVolume = this.safeFloat (ticker, 'volume'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (trade, 'book'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } const side = this.safeString2 (trade, 'side', 'maker_side'); let amount = this.safeFloat2 (trade, 'amount', 'major'); if (amount !== undefined) { amount = Math.abs (amount); } let fee = undefined; const feeCost = this.safeFloat (trade, 'fees_amount'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'fees_currency'); const feeCurrency = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrency, }; } let cost = this.safeFloat (trade, 'minor'); if (cost !== undefined) { cost = Math.abs (cost); } const price = this.safeFloat (trade, 'price'); const orderId = this.safeString (trade, 'oid'); const id = this.safeString (trade, 'tid'); return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'book': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response['payload'], market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = 25, params = {}) { await this.loadMarkets (); const market = this.market (symbol); // the don't support fetching trades starting from a date yet // use the `marker` extra param for that // this is not a typo, the variable name is 'marker' (don't confuse with 'market') const markerInParams = ('marker' in params); // warn the user with an exception if the user wants to filter // starting from since timestamp, but does not set the trade id with an extra 'marker' param if ((since !== undefined) && !markerInParams) { throw new ExchangeError (this.id + ' fetchMyTrades does not support fetching trades starting from a timestamp with the `since` argument, use the `marker` extra param to filter starting from an integer trade id'); } // convert it to an integer unconditionally if (markerInParams) { params = this.extend (params, { 'marker': parseInt (params['marker']), }); } const request = { 'book': market['id'], 'limit': limit, // default = 25, max = 100 // 'sort': 'desc', // default = desc // 'marker': id, // integer id to start from }; const response = await this.privateGetUserTrades (this.extend (request, params)); return this.parseTrades (response['payload'], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'book': this.marketId (symbol), 'side': side, 'type': type, 'major': this.amountToPrecision (symbol, amount), }; if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostOrders (this.extend (request, params)); const id = this.safeString (response['payload'], 'oid'); return { 'info': response, 'id': id, }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'oid': id, }; return await this.privateDeleteOrdersOid (this.extend (request, params)); } parseOrderStatus (status) { const statuses = { 'partial-fill': 'open', // this is a common substitution in ccxt 'completed': 'closed', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const id = this.safeString (order, 'oid'); const side = this.safeString (order, 'side'); const status = this.parseOrderStatus (this.safeString (order, 'status')); let symbol = undefined; const marketId = this.safeString (order, 'book'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const orderType = this.safeString (order, 'type'); const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'original_amount'); const remaining = this.safeFloat (order, 'unfilled_amount'); let filled = undefined; if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; } } const clientOrderId = this.safeString (order, 'client_id'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': orderType, 'side': side, 'price': price, 'amount': amount, 'cost': undefined, 'remaining': remaining, 'filled': filled, 'status': status, 'fee': undefined, 'average': undefined, 'trades': undefined, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = 25, params = {}) { await this.loadMarkets (); const market = this.market (symbol); // the don't support fetching trades starting from a date yet // use the `marker` extra param for that // this is not a typo, the variable name is 'marker' (don't confuse with 'market') const markerInParams = ('marker' in params); // warn the user with an exception if the user wants to filter // starting from since timestamp, but does not set the trade id with an extra 'marker' param if ((since !== undefined) && !markerInParams) { throw new ExchangeError (this.id + ' fetchOpenOrders does not support fetching orders starting from a timestamp with the `since` argument, use the `marker` extra param to filter starting from an integer trade id'); } // convert it to an integer unconditionally if (markerInParams) { params = this.extend (params, { 'marker': parseInt (params['marker']), }); } const request = { 'book': market['id'], 'limit': limit, // default = 25, max = 100 // 'sort': 'desc', // default = desc // 'marker': id, // integer id to start from }; const response = await this.privateGetOpenOrders (this.extend (request, params)); const orders = this.parseOrders (response['payload'], market, since, limit); return orders; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const response = await this.privateGetOrdersOid ({ 'oid': id, }); const payload = this.safeValue (response, 'payload'); if (Array.isArray (payload)) { const numOrders = response['payload'].length; if (numOrders === 1) { return this.parseOrder (payload[0]); } } throw new OrderNotFound (this.id + ': The order ' + id + ' not found.'); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'oid': id, }; const response = await this.privateGetOrderTradesOid (this.extend (request, params)); return this.parseTrades (response['payload'], market); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'fund_currency': currency['id'], }; const response = await this.privateGetFundingDestination (this.extend (request, params)); let address = this.safeString (response['payload'], 'account_identifier'); let tag = undefined; if (code === 'XRP') { const parts = address.split ('?dt='); address = parts[0]; tag = parts[1]; } this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const methods = { 'BTC': 'Bitcoin', 'ETH': 'Ether', 'XRP': 'Ripple', 'BCH': 'Bcash', 'LTC': 'Litecoin', }; const method = (code in methods) ? methods[code] : undefined; if (method === undefined) { throw new ExchangeError (this.id + ' not valid withdraw coin: ' + code); } const request = { 'amount': amount, 'address': address, 'destination_tag': tag, }; const classMethod = 'privatePost' + method + 'Withdrawal'; const response = await this[classMethod] (this.extend (request, params)); return { 'info': response, 'id': this.safeString (response['payload'], 'wid'), }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let endpoint = '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (method === 'GET') { if (Object.keys (query).length) { endpoint += '?' + this.urlencode (query); } } const url = this.urls['api'] + endpoint; if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); let request = [ nonce, method, endpoint ].join (''); if (method !== 'GET') { if (Object.keys (query).length) { body = this.json (query); request += body; } } const signature = this.hmac (this.encode (request), this.encode (this.secret)); const auth = this.apiKey + ':' + nonce + ':' + signature; headers = { 'Authorization': 'Bitso ' + auth, 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('success' in response) { // // {"success":false,"error":{"code":104,"message":"Cannot perform request - nonce must be higher than 1520307203724237"}} // let success = this.safeValue (response, 'success', false); if (typeof success === 'string') { if ((success === 'true') || (success === '1')) { success = true; } else { success = false; } } if (!success) { const feedback = this.id + ' ' + this.json (response); const error = this.safeValue (response, 'error'); if (error === undefined) { throw new ExchangeError (feedback); } const code = this.safeString (error, 'code'); this.throwExactlyMatchedException (this.exceptions, code, feedback); throw new ExchangeError (feedback); } } } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('success' in response) { if (response['success']) { return response; } } throw new ExchangeError (this.id + ' ' + this.json (response)); } }; },{"./base/Exchange":7,"./base/errors":9}],41:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { AuthenticationError, ExchangeError, NotSupported, PermissionDenied, InvalidNonce, OrderNotFound, InsufficientFunds, InvalidAddress, InvalidOrder } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitstamp extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitstamp', 'name': 'Bitstamp', 'countries': [ 'GB' ], 'rateLimit': 1000, 'version': 'v2', 'userAgent': this.userAgents['chrome'], 'pro': true, 'has': { 'CORS': true, 'fetchDepositAddress': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchMyTrades': true, 'fetchTransactions': true, 'fetchWithdrawals': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg', 'api': { 'public': 'https://www.bitstamp.net/api', 'private': 'https://www.bitstamp.net/api', 'v1': 'https://www.bitstamp.net/api', }, 'www': 'https://www.bitstamp.net', 'doc': 'https://www.bitstamp.net/api', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': true, }, 'api': { 'public': { 'get': [ 'order_book/{pair}/', 'ticker_hour/{pair}/', 'ticker/{pair}/', 'transactions/{pair}/', 'trading-pairs-info/', ], }, 'private': { 'post': [ 'balance/', 'balance/{pair}/', 'bch_withdrawal/', 'bch_address/', 'user_transactions/', 'user_transactions/{pair}/', 'open_orders/all/', 'open_orders/{pair}/', 'order_status/', 'cancel_order/', 'buy/{pair}/', 'buy/market/{pair}/', 'buy/instant/{pair}/', 'sell/{pair}/', 'sell/market/{pair}/', 'sell/instant/{pair}/', 'ltc_withdrawal/', 'ltc_address/', 'eth_withdrawal/', 'eth_address/', 'xrp_withdrawal/', 'xrp_address/', 'transfer-to-main/', 'transfer-from-main/', 'withdrawal-requests/', 'withdrawal/open/', 'withdrawal/status/', 'withdrawal/cancel/', 'liquidation_address/new/', 'liquidation_address/info/', ], }, 'v1': { 'post': [ 'bitcoin_deposit_address/', 'unconfirmed_btc/', 'bitcoin_withdrawal/', 'ripple_withdrawal/', 'ripple_address/', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': 0.5 / 100, 'maker': 0.5 / 100, 'tiers': { 'taker': [ [0, 0.5 / 100], [20000, 0.25 / 100], [100000, 0.24 / 100], [200000, 0.22 / 100], [400000, 0.20 / 100], [600000, 0.15 / 100], [1000000, 0.14 / 100], [2000000, 0.13 / 100], [4000000, 0.12 / 100], [20000000, 0.11 / 100], [20000001, 0.10 / 100], ], 'maker': [ [0, 0.5 / 100], [20000, 0.25 / 100], [100000, 0.24 / 100], [200000, 0.22 / 100], [400000, 0.20 / 100], [600000, 0.15 / 100], [1000000, 0.14 / 100], [2000000, 0.13 / 100], [4000000, 0.12 / 100], [20000000, 0.11 / 100], [20000001, 0.10 / 100], ], }, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BTC': 0, 'BCH': 0, 'LTC': 0, 'ETH': 0, 'XRP': 0, 'USD': 25, 'EUR': 0.90, }, 'deposit': { 'BTC': 0, 'BCH': 0, 'LTC': 0, 'ETH': 0, 'XRP': 0, 'USD': 25, 'EUR': 0, }, }, }, 'exceptions': { 'exact': { 'No permission found': PermissionDenied, 'API key not found': AuthenticationError, 'IP address not allowed': PermissionDenied, 'Invalid nonce': InvalidNonce, 'Invalid signature': AuthenticationError, 'Authentication failed': AuthenticationError, 'Missing key, signature and nonce parameters': AuthenticationError, 'Your account is frozen': PermissionDenied, 'Please update your profile with your FATCA information, before using API.': PermissionDenied, 'Order not found': OrderNotFound, 'Price is more than 20% below market price.': InvalidOrder, }, 'broad': { 'Minimum order size is': InvalidOrder, // Minimum order size is 5.0 EUR. 'Check your account balance for details.': InsufficientFunds, // You have only 0.00100000 BTC available. Check your account balance for details. 'Ensure this value has at least': InvalidAddress, // Ensure this value has at least 25 characters (it has 4). }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetTradingPairsInfo (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const name = this.safeString (market, 'name'); let [ base, quote ] = name.split ('/'); const baseId = base.toLowerCase (); const quoteId = quote.toLowerCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const symbol = base + '/' + quote; const symbolId = baseId + '_' + quoteId; const id = this.safeString (market, 'url_symbol'); const precision = { 'amount': market['base_decimals'], 'price': market['counter_decimals'], }; const parts = market['minimum_order'].split (' '); const cost = parts[0]; // let [ cost, currency ] = market['minimum_order'].split (' '); const active = (market['trading'] === 'Enabled'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'symbolId': symbolId, 'info': market, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': parseFloat (cost), 'max': undefined, }, }, }); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; const response = await this.publicGetOrderBookPair (this.extend (request, params)); // // { // "timestamp": "1583652948", // "microtimestamp": "1583652948955826", // "bids": [ // [ "8750.00", "1.33685271" ], // [ "8749.39", "0.07700000" ], // [ "8746.98", "0.07400000" ], // ] // "asks": [ // [ "8754.10", "1.51995636" ], // [ "8754.71", "1.40000000" ], // [ "8754.72", "2.50000000" ], // ] // } // const microtimestamp = this.safeInteger (response, 'microtimestamp'); const timestamp = parseInt (microtimestamp / 1000); const orderbook = this.parseOrderBook (response, timestamp); orderbook['nonce'] = microtimestamp; return orderbook; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; const ticker = await this.publicGetTickerPair (this.extend (request, params)); const timestamp = this.safeTimestamp (ticker, 'timestamp'); const vwap = this.safeFloat (ticker, 'vwap'); const baseVolume = this.safeFloat (ticker, 'volume'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } getCurrencyIdFromTransaction (transaction) { // // { // "fee": "0.00000000", // "btc_usd": "0.00", // "datetime": XXX, // "usd": 0.0, // "btc": 0.0, // "eth": "0.05000000", // "type": "0", // "id": XXX, // "eur": 0.0 // } // const currencyId = this.safeStringLower (transaction, 'currency'); if (currencyId !== undefined) { return currencyId; } transaction = this.omit (transaction, [ 'fee', 'price', 'datetime', 'type', 'status', 'id', ]); const ids = Object.keys (transaction); for (let i = 0; i < ids.length; i++) { const id = ids[i]; if (id.indexOf ('_') < 0) { const value = this.safeFloat (transaction, id); if ((value !== undefined) && (value !== 0)) { return id; } } } return undefined; } getMarketFromTrade (trade) { trade = this.omit (trade, [ 'fee', 'price', 'datetime', 'tid', 'type', 'order_id', 'side', ]); const currencyIds = Object.keys (trade); const numCurrencyIds = currencyIds.length; if (numCurrencyIds > 2) { throw new ExchangeError (this.id + ' getMarketFromTrade too many keys: ' + this.json (currencyIds) + ' in the trade: ' + this.json (trade)); } if (numCurrencyIds === 2) { let marketId = currencyIds[0] + currencyIds[1]; if (marketId in this.markets_by_id) { return this.markets_by_id[marketId]; } marketId = currencyIds[1] + currencyIds[0]; if (marketId in this.markets_by_id) { return this.markets_by_id[marketId]; } } return undefined; } getMarketFromTrades (trades) { const tradesBySymbol = this.indexBy (trades, 'symbol'); const symbols = Object.keys (tradesBySymbol); const numSymbols = symbols.length; if (numSymbols === 1) { return this.markets[symbols[0]]; } return undefined; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // date: '1551814435', // tid: '83581898', // price: '0.03532850', // type: '1', // amount: '0.85945907' // }, // // fetchMyTrades, trades returned within fetchOrder (private) // // { // "usd": "6.0134400000000000", // "price": "4008.96000000", // "datetime": "2019-03-28 23:07:37.233599", // "fee": "0.02", // "btc": "0.00150000", // "tid": 84452058, // "type": 2 // } // // from fetchOrder: // { fee: '0.000019', // price: '0.00015803', // datetime: '2018-01-07 10:45:34.132551', // btc: '0.0079015000000000', // tid: 42777395, // type: 2, //(0 - deposit; 1 - withdrawal; 2 - market trade) NOT buy/sell // xrp: '50.00000000' } const id = this.safeString2 (trade, 'id', 'tid'); let symbol = undefined; let side = undefined; let price = this.safeFloat (trade, 'price'); let amount = this.safeFloat (trade, 'amount'); const orderId = this.safeString (trade, 'order_id'); const type = undefined; let cost = this.safeFloat (trade, 'cost'); if (market === undefined) { const keys = Object.keys (trade); for (let i = 0; i < keys.length; i++) { if (keys[i].indexOf ('_') >= 0) { const marketId = keys[i].replace ('_', ''); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } } // if the market is still not defined // try to deduce it from used keys if (market === undefined) { market = this.getMarketFromTrade (trade); } } const feeCost = this.safeFloat (trade, 'fee'); let feeCurrency = undefined; if (market !== undefined) { price = this.safeFloat (trade, market['symbolId'], price); amount = this.safeFloat (trade, market['baseId'], amount); cost = this.safeFloat (trade, market['quoteId'], cost); feeCurrency = market['quote']; symbol = market['symbol']; } let timestamp = this.safeString2 (trade, 'date', 'datetime'); if (timestamp !== undefined) { if (timestamp.indexOf (' ') >= 0) { // iso8601 timestamp = this.parse8601 (timestamp); } else { // string unix epoch in seconds timestamp = parseInt (timestamp); timestamp = timestamp * 1000; } } // if it is a private trade if ('id' in trade) { if (amount !== undefined) { if (amount < 0) { side = 'sell'; amount = -amount; } else { side = 'buy'; } } } else { side = this.safeString (trade, 'type'); if (side === '1') { side = 'sell'; } else if (side === '0') { side = 'buy'; } } if (cost === undefined) { if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } } if (cost !== undefined) { cost = Math.abs (cost); } let fee = undefined; if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': feeCurrency, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'time': 'hour', }; const response = await this.publicGetTransactionsPair (this.extend (request, params)); // // [ // { // date: '1551814435', // tid: '83581898', // price: '0.03532850', // type: '1', // amount: '0.85945907' // }, // { // date: '1551814434', // tid: '83581896', // price: '0.03532851', // type: '1', // amount: '11.34130961' // }, // ] // return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const balance = await this.privatePostBalance (params); const result = { 'info': balance }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currency = this.currency (code); const currencyId = currency['id']; const account = this.account (); account['free'] = this.safeFloat (balance, currencyId + '_available'); account['used'] = this.safeFloat (balance, currencyId + '_reserved'); account['total'] = this.safeFloat (balance, currencyId + '_balance'); result[code] = account; } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let method = 'privatePost' + this.capitalize (side); const request = { 'pair': market['id'], 'amount': this.amountToPrecision (symbol, amount), }; if (type === 'market') { method += 'Market'; } else { request['price'] = this.priceToPrecision (symbol, price); } method += 'Pair'; const response = await this[method] (this.extend (request, params)); const order = this.parseOrder (response, market); return this.extend (order, { 'type': type, }); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; return await this.privatePostCancelOrder (this.extend (request, params)); } parseOrderStatus (status) { const statuses = { 'In Queue': 'open', 'Open': 'open', 'Finished': 'closed', 'Canceled': 'canceled', }; return this.safeString (statuses, status, status); } async fetchOrderStatus (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privatePostOrderStatus (this.extend (request, params)); return this.parseOrderStatus (this.safeString (response, 'status')); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'id': id }; const response = await this.privatePostOrderStatus (this.extend (request, params)); // // { // "status": "Finished", // "id": 3047704374, // "transactions": [ // { // "usd": "6.0134400000000000", // "price": "4008.96000000", // "datetime": "2019-03-28 23:07:37.233599", // "fee": "0.02", // "btc": "0.00150000", // "tid": 84452058, // "type": 2 // } // ] // } return this.parseOrder (response, market); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let method = 'privatePostUserTransactions'; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; method += 'Pair'; } if (limit !== undefined) { request['limit'] = limit; } const response = await this[method] (this.extend (request, params)); const result = this.filterBy (response, 'type', '2'); return this.parseTrades (result, market, since, limit); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privatePostUserTransactions (this.extend (request, params)); // // [ // { // "fee": "0.00000000", // "btc_usd": "0.00", // "id": 1234567894, // "usd": 0, // "btc": 0, // "datetime": "2018-09-08 09:00:31", // "type": "1", // "xrp": "-20.00000000", // "eur": 0, // }, // { // "fee": "0.00000000", // "btc_usd": "0.00", // "id": 1134567891, // "usd": 0, // "btc": 0, // "datetime": "2018-09-07 18:47:52", // "type": "0", // "xrp": "20.00000000", // "eur": 0, // }, // ] // let currency = undefined; if (code !== undefined) { currency = this.currency (code); } const transactions = this.filterByArray (response, 'type', [ '0', '1' ], false); return this.parseTransactions (transactions, currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (since !== undefined) { request['timedelta'] = this.milliseconds () - since; } const response = await this.privatePostWithdrawalRequests (this.extend (request, params)); // // [ // { // status: 2, // datetime: '2018-10-17 10:58:13', // currency: 'BTC', // amount: '0.29669259', // address: 'aaaaa', // type: 1, // id: 111111, // transaction_id: 'xxxx', // }, // { // status: 2, // datetime: '2018-10-17 10:55:17', // currency: 'ETH', // amount: '1.11010664', // address: 'aaaa', // type: 16, // id: 222222, // transaction_id: 'xxxxx', // }, // ] // return this.parseTransactions (response, undefined, since, limit); } parseTransaction (transaction, currency = undefined) { // // fetchTransactions // // { // "fee": "0.00000000", // "btc_usd": "0.00", // "id": 1234567894, // "usd": 0, // "btc": 0, // "datetime": "2018-09-08 09:00:31", // "type": "1", // "xrp": "-20.00000000", // "eur": 0, // } // // fetchWithdrawals // // { // status: 2, // datetime: '2018-10-17 10:58:13', // currency: 'BTC', // amount: '0.29669259', // address: 'aaaaa', // type: 1, // id: 111111, // transaction_id: 'xxxx', // } // // { // "id": 3386432, // "type": 14, // "amount": "863.21332500", // "status": 2, // "address": "rE1sdh25BJQ3qFwngiTBwaq3zPGGYcrjp1?dt=1455", // "currency": "XRP", // "datetime": "2018-01-05 15:27:55", // "transaction_id": "001743B03B0C79BA166A064AC0142917B050347B4CB23BA2AB4B91B3C5608F4C" // } // const timestamp = this.parse8601 (this.safeString (transaction, 'datetime')); const id = this.safeString (transaction, 'id'); const currencyId = this.getCurrencyIdFromTransaction (transaction); const code = this.safeCurrencyCode (currencyId, currency); const feeCost = this.safeFloat (transaction, 'fee'); let feeCurrency = undefined; let amount = undefined; if ('amount' in transaction) { amount = this.safeFloat (transaction, 'amount'); } else if (currency !== undefined) { amount = this.safeFloat (transaction, currency['id'], amount); feeCurrency = currency['code']; } else if ((code !== undefined) && (currencyId !== undefined)) { amount = this.safeFloat (transaction, currencyId, amount); feeCurrency = code; } if (amount !== undefined) { // withdrawals have a negative amount amount = Math.abs (amount); } let status = 'ok'; if ('status' in transaction) { status = this.parseTransactionStatus (this.safeString (transaction, 'status')); } let type = undefined; if ('type' in transaction) { // from fetchTransactions const rawType = this.safeString (transaction, 'type'); if (rawType === '0') { type = 'deposit'; } else if (rawType === '1') { type = 'withdrawal'; } } else { // from fetchWithdrawals type = 'withdrawal'; } const txid = this.safeString (transaction, 'transaction_id'); let tag = undefined; let address = this.safeString (transaction, 'address'); if (address !== undefined) { // dt (destination tag) is embedded into the address field const addressParts = address.split ('?dt='); const numParts = addressParts.length; if (numParts > 1) { address = addressParts[0]; tag = addressParts[1]; } } const addressFrom = undefined; const addressTo = address; const tagFrom = undefined; const tagTo = tag; let fee = undefined; if (feeCost !== undefined) { fee = { 'currency': feeCurrency, 'cost': feeCost, 'rate': undefined, }; } return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'addressFrom': addressFrom, 'addressTo': addressTo, 'address': address, 'tagFrom': tagFrom, 'tagTo': tagTo, 'tag': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': fee, }; } parseTransactionStatus (status) { // withdrawals: // 0 (open), 1 (in process), 2 (finished), 3 (canceled) or 4 (failed). const statuses = { '0': 'pending', // Open '1': 'pending', // In process '2': 'ok', // Finished '3': 'canceled', // Canceled '4': 'failed', // Failed }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // from fetch order: // { status: 'Finished', // id: 731693945, // transactions: // [ { fee: '0.000019', // price: '0.00015803', // datetime: '2018-01-07 10:45:34.132551', // btc: '0.0079015000000000', // tid: 42777395, // type: 2, // xrp: '50.00000000' } ] } // // partially filled order: // { "id": 468646390, // "status": "Canceled", // "transactions": [{ // "eth": "0.23000000", // "fee": "0.09", // "tid": 25810126, // "usd": "69.8947000000000000", // "type": 2, // "price": "303.89000000", // "datetime": "2017-11-11 07:22:20.710567" // }]} // // from create order response: // { // price: '0.00008012', // currency_pair: 'XRP/BTC', // datetime: '2019-01-31 21:23:36', // amount: '15.00000000', // type: '0', // id: '2814205012' // } // const id = this.safeString (order, 'id'); let side = this.safeString (order, 'type'); if (side !== undefined) { side = (side === '1') ? 'sell' : 'buy'; } // there is no timestamp from fetchOrder const timestamp = this.parse8601 (this.safeString (order, 'datetime')); let lastTradeTimestamp = undefined; let symbol = undefined; let marketId = this.safeStringLower (order, 'currency_pair'); if (marketId !== undefined) { marketId = marketId.replace ('/', ''); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } } let amount = this.safeFloat (order, 'amount'); let filled = 0.0; const trades = []; const transactions = this.safeValue (order, 'transactions', []); let feeCost = undefined; let cost = undefined; const numTransactions = transactions.length; if (numTransactions > 0) { feeCost = 0.0; for (let i = 0; i < numTransactions; i++) { const trade = this.parseTrade (this.extend ({ 'order_id': id, 'side': side, }, transactions[i]), market); filled = this.sum (filled, trade['amount']); feeCost = this.sum (feeCost, trade['fee']['cost']); if (cost === undefined) { cost = 0.0; } cost = this.sum (cost, trade['cost']); trades.push (trade); } lastTradeTimestamp = trades[numTransactions - 1]['timestamp']; } const status = this.parseOrderStatus (this.safeString (order, 'status')); if ((status === 'closed') && (amount === undefined)) { amount = filled; } let remaining = undefined; if (amount !== undefined) { remaining = amount - filled; } let price = this.safeFloat (order, 'price'); if (market === undefined) { market = this.getMarketFromTrades (trades); } let feeCurrency = undefined; if (market !== undefined) { if (symbol === undefined) { symbol = market['symbol']; } feeCurrency = market['quote']; } if (cost === undefined) { if (price !== undefined) { cost = price * filled; } } else if (price === undefined) { if (filled > 0) { price = cost / filled; } } let fee = undefined; if (feeCost !== undefined) { if (feeCurrency !== undefined) { fee = { 'cost': feeCost, 'currency': feeCurrency, }; } } return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': lastTradeTimestamp, 'status': status, 'symbol': symbol, 'type': undefined, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': trades, 'fee': fee, 'info': order, 'average': undefined, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { let market = undefined; await this.loadMarkets (); if (symbol !== undefined) { market = this.market (symbol); } const response = await this.privatePostOpenOrdersAll (params); // [ // { // price: '0.00008012', // currency_pair: 'XRP/BTC', // datetime: '2019-01-31 21:23:36', // amount: '15.00000000', // type: '0', // id: '2814205012', // } // ] // const result = []; for (let i = 0; i < response.length; i++) { const order = this.parseOrder (response[i], market); result.push (this.extend (order, { 'status': 'open', 'type': 'limit', })); } if (symbol === undefined) { return this.filterBySinceLimit (result, since, limit); } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } getCurrencyName (code) { if (code === 'BTC') { return 'bitcoin'; } return code.toLowerCase (); } isFiat (code) { if (code === 'USD') { return true; } if (code === 'EUR') { return true; } return false; } async fetchDepositAddress (code, params = {}) { if (this.isFiat (code)) { throw new NotSupported (this.id + ' fiat fetchDepositAddress() for ' + code + ' is not implemented yet'); } const name = this.getCurrencyName (code); const v1 = (code === 'BTC'); let method = v1 ? 'v1' : 'private'; // v1 or v2 method += 'Post' + this.capitalize (name); method += v1 ? 'Deposit' : ''; method += 'Address'; let response = await this[method] (params); if (v1) { response = JSON.parse (response); } const address = v1 ? response : this.safeString (response, 'address'); const tag = v1 ? undefined : this.safeString (response, 'destination_tag'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); if (this.isFiat (code)) { throw new NotSupported (this.id + ' fiat withdraw() for ' + code + ' is not implemented yet'); } const name = this.getCurrencyName (code); const request = { 'amount': amount, 'address': address, }; const v1 = (code === 'BTC'); let method = v1 ? 'v1' : 'private'; // v1 or v2 method += 'Post' + this.capitalize (name) + 'Withdrawal'; if (code === 'XRP') { if (tag !== undefined) { request['destination_tag'] = tag; } } const response = await this[method] (this.extend (request, params)); return { 'info': response, 'id': response['id'], }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/'; if (api !== 'v1') { url += this.version + '/'; } url += this.implodeParams (path, params); let query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const authVersion = this.safeValue (this.options, 'auth', 'v2'); if ((authVersion === 'v1') || (api === 'v1')) { const nonce = this.nonce ().toString (); const auth = nonce + this.uid + this.apiKey; const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret))); query = this.extend ({ 'key': this.apiKey, 'signature': signature.toUpperCase (), 'nonce': nonce, }, query); body = this.urlencode (query); headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } else { const xAuth = 'BITSTAMP ' + this.apiKey; const xAuthNonce = this.uuid (); const xAuthTimestamp = this.milliseconds ().toString (); const xAuthVersion = 'v2'; let contentType = ''; headers = { 'X-Auth': xAuth, 'X-Auth-Nonce': xAuthNonce, 'X-Auth-Timestamp': xAuthTimestamp, 'X-Auth-Version': xAuthVersion, }; if (method === 'POST') { if (Object.keys (query).length) { body = this.urlencode (query); contentType = 'application/x-www-form-urlencoded'; headers['Content-Type'] = contentType; } else { // sending an empty POST request will trigger // an API0020 error returned by the exchange // therefore for empty requests we send a dummy object // https://github.com/ccxt/ccxt/issues/6846 body = this.urlencode ({ 'foo': 'bar' }); contentType = 'application/x-www-form-urlencoded'; headers['Content-Type'] = contentType; } } const authBody = body ? body : ''; const auth = xAuth + method + url.replace ('https://', '') + contentType + xAuthNonce + xAuthTimestamp + xAuthVersion + authBody; const signature = this.hmac (this.encode (auth), this.encode (this.secret)); headers['X-Auth-Signature'] = signature; } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } // // {"error": "No permission found"} // fetchDepositAddress returns this on apiKeys that don't have the permission required // {"status": "error", "reason": {"__all__": ["Minimum order size is 5.0 EUR."]}} // reuse of a nonce gives: { status: 'error', reason: 'Invalid nonce', code: 'API0004' } const status = this.safeString (response, 'status'); const error = this.safeValue (response, 'error'); if ((status === 'error') || (error !== undefined)) { let errors = []; if (typeof error === 'string') { errors.push (error); } else if (error !== undefined) { const keys = Object.keys (error); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = this.safeValue (error, key); if (Array.isArray (value)) { errors = this.arrayConcat (errors, value); } else { errors.push (value); } } } const reason = this.safeValue (response, 'reason', {}); if (typeof reason === 'string') { errors.push (reason); } else { const all = this.safeValue (reason, '__all__', []); for (let i = 0; i < all.length; i++) { errors.push (all[i]); } } const code = this.safeString (response, 'code'); if (code === 'API0005') { throw new AuthenticationError (this.id + ' invalid signature, use the uid for the main account if you have subaccounts'); } const feedback = this.id + ' ' + body; for (let i = 0; i < errors.length; i++) { const value = errors[i]; this.throwExactlyMatchedException (this.exceptions['exact'], value, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], value, feedback); } throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9}],42:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadSymbol, ExchangeError, NotSupported } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitstamp1 extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitstamp1', 'name': 'Bitstamp', 'countries': [ 'GB' ], 'rateLimit': 1000, 'version': 'v1', 'has': { 'CORS': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg', 'api': 'https://www.bitstamp.net/api', 'www': 'https://www.bitstamp.net', 'doc': 'https://www.bitstamp.net/api', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': true, }, 'api': { 'public': { 'get': [ 'ticker', 'ticker_hour', 'order_book', 'transactions', 'eur_usd', ], }, 'private': { 'post': [ 'balance', 'user_transactions', 'open_orders', 'order_status', 'cancel_order', 'cancel_all_orders', 'buy', 'sell', 'bitcoin_deposit_address', 'unconfirmed_btc', 'ripple_withdrawal', 'ripple_address', 'withdrawal_requests', 'bitcoin_withdrawal', ], }, }, 'markets': { 'BTC/USD': { 'id': 'btcusd', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'btc', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, 'BTC/EUR': { 'id': 'btceur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'btc', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, 'EUR/USD': { 'id': 'eurusd', 'symbol': 'EUR/USD', 'base': 'EUR', 'quote': 'USD', 'baseId': 'eur', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, 'XRP/USD': { 'id': 'xrpusd', 'symbol': 'XRP/USD', 'base': 'XRP', 'quote': 'USD', 'baseId': 'xrp', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, 'XRP/EUR': { 'id': 'xrpeur', 'symbol': 'XRP/EUR', 'base': 'XRP', 'quote': 'EUR', 'baseId': 'xrp', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, 'XRP/BTC': { 'id': 'xrpbtc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, 'LTC/USD': { 'id': 'ltcusd', 'symbol': 'LTC/USD', 'base': 'LTC', 'quote': 'USD', 'baseId': 'ltc', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, 'LTC/EUR': { 'id': 'ltceur', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'ltc', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, 'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, 'ETH/USD': { 'id': 'ethusd', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD', 'baseId': 'eth', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, 'ETH/EUR': { 'id': 'etheur', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR', 'baseId': 'eth', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, 'ETH/BTC': { 'id': 'ethbtc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, }, }); } async fetchOrderBook (symbol, limit = undefined, params = {}) { if (symbol !== 'BTC/USD') { throw new ExchangeError (this.id + ' ' + this.version + " fetchOrderBook doesn't support " + symbol + ', use it for BTC/USD only'); } await this.loadMarkets (); const orderbook = await this.publicGetOrderBook (params); const timestamp = this.safeTimestamp (orderbook, 'timestamp'); return this.parseOrderBook (orderbook, timestamp); } async fetchTicker (symbol, params = {}) { if (symbol !== 'BTC/USD') { throw new ExchangeError (this.id + ' ' + this.version + " fetchTicker doesn't support " + symbol + ', use it for BTC/USD only'); } await this.loadMarkets (); const ticker = await this.publicGetTicker (params); const timestamp = this.safeTimestamp (ticker, 'timestamp'); const vwap = this.safeFloat (ticker, 'vwap'); const baseVolume = this.safeFloat (ticker, 'volume'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp2 (trade, 'date', 'datetime'); const side = (trade['type'] === 0) ? 'buy' : 'sell'; const orderId = this.safeString (trade, 'order_id'); if ('currency_pair' in trade) { if (trade['currency_pair'] in this.markets_by_id) { market = this.markets_by_id[trade['currency_pair']]; } } const id = this.safeString (trade, 'tid'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { if (symbol !== 'BTC/USD') { throw new BadSymbol (this.id + ' ' + this.version + " fetchTrades doesn't support " + symbol + ', use it for BTC/USD only'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'time': 'minute', }; const response = await this.publicGetTransactions (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { const balance = await this.privatePostBalance (params); const result = { 'info': balance }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currency = this.currency (code); const currencyId = currency['id']; const account = this.account (); account['free'] = this.safeFloat (balance, currencyId + '_available'); account['used'] = this.safeFloat (balance, currencyId + '_reserved'); account['total'] = this.safeFloat (balance, currencyId + '_balance'); result[code] = account; } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type !== 'limit') { throw new ExchangeError (this.id + ' ' + this.version + ' accepts limit orders only'); } if (symbol !== 'BTC/USD') { throw new ExchangeError (this.id + ' v1 supports BTC/USD orders only'); } await this.loadMarkets (); const method = 'privatePost' + this.capitalize (side); const request = { 'amount': amount, 'price': price, }; const response = await this[method] (this.extend (request, params)); const id = this.safeString (response, 'id'); return { 'info': response, 'id': id, }; } async cancelOrder (id, symbol = undefined, params = {}) { return await this.privatePostCancelOrder ({ 'id': id }); } parseOrderStatus (status) { const statuses = { 'In Queue': 'open', 'Open': 'open', 'Finished': 'closed', 'Canceled': 'canceled', }; return this.safeString (statuses, status, status); } async fetchOrderStatus (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privatePostOrderStatus (this.extend (request, params)); return this.parseOrderStatus (response); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const pair = market ? market['id'] : 'all'; const request = { 'id': pair, }; const response = await this.privatePostOpenOrdersId (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { throw new NotSupported (this.id + ' fetchOrder is not implemented yet'); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.implodeParams (path, params); let query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const auth = nonce + this.uid + this.apiKey; const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret))); query = this.extend ({ 'key': this.apiKey, 'signature': signature.toUpperCase (), 'nonce': nonce, }, query); body = this.urlencode (query); headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('status' in response) { if (response['status'] === 'error') { throw new ExchangeError (this.id + ' ' + this.json (response)); } } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],43:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadSymbol, ExchangeError, ExchangeNotAvailable, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, DDoSProtection, PermissionDenied, AddressPending, OnMaintenance } = require ('./base/errors'); const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class bittrex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bittrex', 'name': 'Bittrex', 'countries': [ 'US' ], 'version': 'v1.1', 'rateLimit': 1500, 'certified': true, 'pro': true, // new metainfo interface 'has': { 'CORS': false, 'createMarketOrder': true, 'fetchDepositAddress': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchMyTrades': 'emulated', 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchTickers': true, 'withdraw': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTransactions': false, }, 'timeframes': { '1m': 'oneMin', '5m': 'fiveMin', '30m': 'thirtyMin', '1h': 'hour', '1d': 'day', }, 'hostname': 'bittrex.com', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766352-cf0b3c26-5ed5-11e7-82b7-f3826b7a97d8.jpg', 'api': { 'public': 'https://{hostname}/api', 'account': 'https://{hostname}/api', 'market': 'https://{hostname}/api', 'v2': 'https://{hostname}/api/v2.0/pub', 'v3': 'https://api.bittrex.com/v3', 'v3public': 'https://api.bittrex.com/v3', }, 'www': 'https://bittrex.com', 'doc': [ 'https://bittrex.github.io/api/', 'https://bittrex.github.io/api/v3', 'https://www.npmjs.com/package/bittrex-node', ], 'fees': [ 'https://bittrex.zendesk.com/hc/en-us/articles/115003684371-BITTREX-SERVICE-FEES-AND-WITHDRAWAL-LIMITATIONS', 'https://bittrex.zendesk.com/hc/en-us/articles/115000199651-What-fees-does-Bittrex-charge-', ], 'referral': 'https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B', }, 'api': { 'v3': { 'get': [ 'account', 'addresses', 'addresses/{currencySymbol}', 'balances', 'balances/{currencySymbol}', 'currencies', 'currencies/{symbol}', 'deposits/open', 'deposits/closed', 'deposits/ByTxId/{txId}', 'deposits/{depositId}', 'orders/closed', 'orders/open', 'orders/{orderId}', 'ping', 'subaccounts/{subaccountId}', 'subaccounts', 'withdrawals/open', 'withdrawals/closed', 'withdrawals/ByTxId/{txId}', 'withdrawals/{withdrawalId}', ], 'post': [ 'addresses', 'orders', 'subaccounts', 'withdrawals', ], 'delete': [ 'orders/{orderId}', 'withdrawals/{withdrawalId}', ], }, 'v3public': { 'get': [ 'markets', 'markets/summaries', 'markets/{marketSymbol}', 'markets/{marketSymbol}/summary', 'markets/{marketSymbol}/orderbook', 'markets/{marketSymbol}/trades', 'markets/{marketSymbol}/ticker', 'markets/{marketSymbol}/candles', ], }, 'v2': { 'get': [ 'currencies/GetBTCPrice', 'currencies/GetWalletHealth', 'general/GetLatestAlert', 'market/GetTicks', 'market/GetLatestTick', 'Markets/GetMarketSummaries', 'market/GetLatestTick', ], }, 'public': { 'get': [ 'currencies', 'markethistory', 'markets', 'marketsummaries', 'marketsummary', 'orderbook', 'ticker', ], }, 'account': { 'get': [ 'balance', 'balances', 'depositaddress', 'deposithistory', 'order', 'orders', 'orderhistory', 'withdrawalhistory', 'withdraw', ], }, 'market': { 'get': [ 'buylimit', 'buymarket', 'cancel', 'openorders', 'selllimit', 'sellmarket', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.0025, 'taker': 0.0025, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BTC': 0.0005, 'LTC': 0.01, 'DOGE': 2, 'VTC': 0.02, 'PPC': 0.02, 'FTC': 0.2, 'RDD': 2, 'NXT': 2, 'DASH': 0.05, 'POT': 0.002, 'BLK': 0.02, 'EMC2': 0.2, 'XMY': 0.2, 'GLD': 0.0002, 'SLR': 0.2, 'GRS': 0.2, }, 'deposit': { 'BTC': 0, 'LTC': 0, 'DOGE': 0, 'VTC': 0, 'PPC': 0, 'FTC': 0, 'RDD': 0, 'NXT': 0, 'DASH': 0, 'POT': 0, 'BLK': 0, 'EMC2': 0, 'XMY': 0, 'GLD': 0, 'SLR': 0, 'GRS': 0, }, }, }, 'exceptions': { 'exact': { // 'Call to Cancel was throttled. Try again in 60 seconds.': DDoSProtection, // 'Call to GetBalances was throttled. Try again in 60 seconds.': DDoSProtection, 'APISIGN_NOT_PROVIDED': AuthenticationError, 'INVALID_SIGNATURE': AuthenticationError, 'INVALID_CURRENCY': ExchangeError, 'INVALID_PERMISSION': AuthenticationError, 'INSUFFICIENT_FUNDS': InsufficientFunds, 'INVALID_CEILING_MARKET_BUY': InvalidOrder, 'INVALID_FIAT_ACCOUNT': InvalidOrder, 'INVALID_ORDER_TYPE': InvalidOrder, 'QUANTITY_NOT_PROVIDED': InvalidOrder, 'MIN_TRADE_REQUIREMENT_NOT_MET': InvalidOrder, 'ORDER_NOT_OPEN': OrderNotFound, 'INVALID_ORDER': InvalidOrder, 'UUID_INVALID': OrderNotFound, 'RATE_NOT_PROVIDED': InvalidOrder, // createLimitBuyOrder ('ETH/BTC', 1, 0) 'INVALID_MARKET': BadSymbol, // {"success":false,"message":"INVALID_MARKET","result":null,"explanation":null} 'WHITELIST_VIOLATION_IP': PermissionDenied, 'DUST_TRADE_DISALLOWED_MIN_VALUE': InvalidOrder, 'RESTRICTED_MARKET': BadSymbol, 'We are down for scheduled maintenance, but we\u2019ll be back up shortly.': OnMaintenance, // {"success":false,"message":"We are down for scheduled maintenance, but we\u2019ll be back up shortly.","result":null,"explanation":null} }, 'broad': { 'throttled': DDoSProtection, 'problem': ExchangeNotAvailable, }, }, 'options': { 'parseOrderStatus': false, 'hasAlreadyAuthenticatedSuccessfully': false, // a workaround for APIKEY_INVALID 'symbolSeparator': '-', // With certain currencies, like // AEON, BTS, GXS, NXT, SBD, STEEM, STR, XEM, XLM, XMR, XRP // an additional tag / memo / payment id is usually required by exchanges. // With Bittrex some currencies imply the "base address + tag" logic. // The base address for depositing is stored on this.currencies[code] // The base address identifies the exchange as the recipient // while the tag identifies the user account within the exchange // and the tag is retrieved with fetchDepositAddress. 'tag': { 'NXT': true, // NXT, BURST 'CRYPTO_NOTE_PAYMENTID': true, // AEON, XMR 'BITSHAREX': true, // BTS 'RIPPLE': true, // XRP 'NEM': true, // XEM 'STELLAR': true, // XLM 'STEEM': true, // SBD, GOLOS // https://github.com/ccxt/ccxt/issues/4794 // 'LISK': true, // LSK }, 'subaccountId': undefined, // see the implementation of fetchClosedOrdersV3 below 'fetchClosedOrdersMethod': 'fetch_closed_orders_v3', 'fetchClosedOrdersFilterBySince': true, // 'createOrderMethod': 'create_order_v1', }, 'commonCurrencies': { 'BITS': 'SWIFT', }, }); } costToPrecision (symbol, cost) { return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); } feeToPrecision (symbol, fee) { return this.decimalToPrecision (fee, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); } async fetchMarkets (params = {}) { const response = await this.v3publicGetMarkets (params); // // [ // { // "symbol":"LTC-BTC", // "baseCurrencySymbol":"LTC", // "quoteCurrencySymbol":"BTC", // "minTradeSize":"0.01686767", // "precision":8, // "status":"ONLINE", // "OFFLINE" // "createdAt":"2014-02-13T00:00:00Z" // }, // { // "symbol":"VDX-USDT", // "baseCurrencySymbol":"VDX", // "quoteCurrencySymbol":"USDT", // "minTradeSize":"300.00000000", // "precision":8, // "status":"ONLINE", // "OFFLINE" // "createdAt":"2019-05-23T00:41:21.843Z", // "notice":"USDT has swapped to an ERC20-based token as of August 5, 2019." // } // ] // const result = []; // const markets = this.safeValue (response, 'result'); for (let i = 0; i < response.length; i++) { const market = response[i]; const baseId = this.safeString (market, 'baseCurrencySymbol'); const quoteId = this.safeString (market, 'quoteCurrencySymbol'); // bittrex v2 uses inverted pairs, v3 uses regular pairs // we use v3 for fetchMarkets and v2 throughout the rest of this implementation // therefore we swap the base ←→ quote here to be v2-compatible // https://github.com/ccxt/ccxt/issues/5634 // const id = this.safeString (market, 'symbol'); const id = quoteId + this.options['symbolSeparator'] + baseId; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const pricePrecision = this.safeInteger (market, 'precision', 8); const precision = { 'amount': 8, 'price': pricePrecision, }; const status = this.safeString (market, 'status'); const active = (status === 'ONLINE'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'info': market, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minTradeSize'), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, }, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.accountGetBalances (params); const balances = this.safeValue (response, 'result'); const result = { 'info': balances }; const indexed = this.indexBy (balances, 'Currency'); const currencyIds = Object.keys (indexed); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); const balance = indexed[currencyId]; account['free'] = this.safeFloat (balance, 'Available'); account['total'] = this.safeFloat (balance, 'Balance'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), 'type': 'both', }; const response = await this.publicGetOrderbook (this.extend (request, params)); let orderbook = response['result']; if ('type' in params) { if (params['type'] === 'buy') { orderbook = { 'buy': response['result'], 'sell': [], }; } else if (params['type'] === 'sell') { orderbook = { 'buy': [], 'sell': response['result'], }; } } return this.parseOrderBook (orderbook, undefined, 'buy', 'sell', 'Rate', 'Quantity'); } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencies (params); // // { // "success": true, // "message": "", // "result": [ // { // "Currency": "BTC", // "CurrencyLong":"Bitcoin", // "MinConfirmation":2, // "TxFee":0.00050000, // "IsActive":true, // "IsRestricted":false, // "CoinType":"BITCOIN", // "BaseAddress":"1N52wHoVR79PMDishab2XmRHsbekCdGquK", // "Notice":null // }, // ..., // ] // } // const currencies = this.safeValue (response, 'result', []); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'Currency'); const code = this.safeCurrencyCode (id); const precision = 8; // default precision, todo: fix "magic constants" const address = this.safeValue (currency, 'BaseAddress'); const fee = this.safeFloat (currency, 'TxFee'); // todo: redesign result[code] = { 'id': id, 'code': code, 'address': address, 'info': currency, 'type': this.safeString (currency, 'CoinType'), 'name': this.safeString (currency, 'CurrencyLong'), 'active': this.safeValue (currency, 'IsActive'), 'fee': fee, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': fee, 'max': undefined, }, }, }; } return result; } parseTicker (ticker, market = undefined) { // // { // "MarketName":"BTC-ETH", // "High":0.02127099, // "Low":0.02035064, // "Volume":10288.40271571, // "Last":0.02070510, // "BaseVolume":214.64663206, // "TimeStamp":"2019-09-18T21:03:59.897", // "Bid":0.02070509, // "Ask":0.02070510, // "OpenBuyOrders":1228, // "OpenSellOrders":5899, // "PrevDay":0.02082823, // "Created":"2015-08-14T09:02:24.817" // } // const timestamp = this.parse8601 (this.safeString (ticker, 'TimeStamp')); let symbol = undefined; const marketId = this.safeString (ticker, 'MarketName'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { symbol = this.parseSymbol (marketId); } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const previous = this.safeFloat (ticker, 'PrevDay'); const last = this.safeFloat (ticker, 'Last'); let change = undefined; let percentage = undefined; if (last !== undefined) { if (previous !== undefined) { change = last - previous; if (previous > 0) { percentage = (change / previous) * 100; } } } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'High'), 'low': this.safeFloat (ticker, 'Low'), 'bid': this.safeFloat (ticker, 'Bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'Ask'), 'askVolume': undefined, 'vwap': undefined, 'open': previous, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'Volume'), 'quoteVolume': this.safeFloat (ticker, 'BaseVolume'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarketsummaries (params); const result = this.safeValue (response, 'result'); const tickers = []; for (let i = 0; i < result.length; i++) { const ticker = this.parseTicker (result[i]); tickers.push (ticker); } return this.filterByArray (tickers, 'symbol', symbols); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetMarketsummary (this.extend (request, params)); // // { // "success":true, // "message":"", // "result":[ // { // "MarketName":"BTC-ETH", // "High":0.02127099, // "Low":0.02035064, // "Volume":10288.40271571, // "Last":0.02070510, // "BaseVolume":214.64663206, // "TimeStamp":"2019-09-18T21:03:59.897", // "Bid":0.02070509, // "Ask":0.02070510, // "OpenBuyOrders":1228, // "OpenSellOrders":5899, // "PrevDay":0.02082823, // "Created":"2015-08-14T09:02:24.817" // } // ] // } // const ticker = response['result'][0]; return this.parseTicker (ticker, market); } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (trade['TimeStamp'] + '+00:00'); let side = undefined; if (trade['OrderType'] === 'BUY') { side = 'buy'; } else if (trade['OrderType'] === 'SELL') { side = 'sell'; } const id = this.safeString2 (trade, 'Id', 'ID'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } let cost = undefined; const price = this.safeFloat (trade, 'Price'); const amount = this.safeFloat (trade, 'Quantity'); if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': undefined, 'type': 'limit', 'takerOrMaker': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetMarkethistory (this.extend (request, params)); if ('result' in response) { if (response['result'] !== undefined) { return this.parseTrades (response['result'], market, since, limit); } } throw new ExchangeError (this.id + ' fetchTrades() returned undefined response'); } parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { const timestamp = this.parse8601 (ohlcv['T'] + '+00:00'); return [ timestamp, ohlcv['O'], ohlcv['H'], ohlcv['L'], ohlcv['C'], ohlcv['V'], ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'tickInterval': this.timeframes[timeframe], 'marketName': market['id'], }; const response = await this.v2GetMarketGetTicks (this.extend (request, params)); if ('result' in response) { if (response['result']) { return this.parseOHLCVs (response['result'], market, timeframe, since, limit); } } } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const response = await this.marketGetOpenorders (this.extend (request, params)); const result = this.safeValue (response, 'result', []); const orders = this.parseOrders (result, market, since, limit); return this.filterBySymbol (orders, symbol); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { const uppercaseType = type.toUpperCase (); const isMarket = (uppercaseType === 'MARKET'); const isCeilingLimit = (uppercaseType === 'CEILING_LIMIT'); const isCeilingMarket = (uppercaseType === 'CEILING_MARKET'); const isV3 = isMarket || isCeilingLimit || isCeilingMarket; const defaultMethod = isV3 ? 'create_order_v3' : 'create_order_v1'; const method = this.safeValue (this.options, 'createOrderMethod', defaultMethod); return await this[method] (symbol, type, side, amount, price, params); } async createOrderV3 (symbol, type, side, amount, price = undefined, params = {}) { // A ceiling order is a market or limit order that allows you to specify // the amount of quote currency you want to spend (or receive, if selling) // instead of the quantity of the market currency (e.g. buy $100 USD of BTC // at the current market BTC price) await this.loadMarkets (); const market = this.market (symbol); const uppercaseType = type.toUpperCase (); const reverseId = market['baseId'] + '-' + market['quoteId']; const request = { 'marketSymbol': reverseId, 'direction': side.toUpperCase (), 'type': uppercaseType, // LIMIT, MARKET, CEILING_LIMIT, CEILING_MARKET // 'quantity': this.amountToPrecision (symbol, amount), // required for limit orders, excluded for ceiling orders // 'ceiling': this.priceToPrecision (symbol, price), // required for ceiling orders, excluded for non-ceiling orders // 'limit': this.priceToPrecision (symbol, price), // required for limit orders, excluded for market orders // 'timeInForce': 'GOOD_TIL_CANCELLED', // IMMEDIATE_OR_CANCEL, FILL_OR_KILL, POST_ONLY_GOOD_TIL_CANCELLED // 'useAwards': false, // optional }; const isCeilingLimit = (uppercaseType === 'CEILING_LIMIT'); const isCeilingMarket = (uppercaseType === 'CEILING_MARKET'); const isCeilingOrder = isCeilingLimit || isCeilingMarket; if (isCeilingOrder) { request['ceiling'] = this.priceToPrecision (symbol, price); // bittrex only accepts IMMEDIATE_OR_CANCEL or FILL_OR_KILL for ceiling orders request['timeInForce'] = 'IMMEDIATE_OR_CANCEL'; } else { request['quantity'] = this.amountToPrecision (symbol, amount); if (uppercaseType === 'LIMIT') { request['limit'] = this.priceToPrecision (symbol, price); request['timeInForce'] = 'GOOD_TIL_CANCELLED'; } else { // bittrex does not allow GOOD_TIL_CANCELLED for market orders request['timeInForce'] = 'IMMEDIATE_OR_CANCEL'; } } const response = await this.v3PostOrders (this.extend (request, params)); // // { // id: 'f03d5e98-b5ac-48fb-8647-dd4db828a297', // marketSymbol: 'BTC-USDT', // direction: 'SELL', // type: 'LIMIT', // quantity: '0.01', // limit: '6000', // timeInForce: 'GOOD_TIL_CANCELLED', // fillQuantity: '0.00000000', // commission: '0.00000000', // proceeds: '0.00000000', // status: 'OPEN', // createdAt: '2020-03-18T02:37:33.42Z', // updatedAt: '2020-03-18T02:37:33.42Z' // } // return this.parseOrderV3 (response, market); } async createOrderV1 (symbol, type, side, amount, price = undefined, params = {}) { if (type !== 'limit') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); const method = 'marketGet' + this.capitalize (side) + type; const request = { 'market': market['id'], 'quantity': this.amountToPrecision (symbol, amount), 'rate': this.priceToPrecision (symbol, price), }; // if (type == 'limit') // order['rate'] = this.priceToPrecision (symbol, price); const response = await this[method] (this.extend (request, params)); const orderIdField = this.getOrderIdField (); const orderId = this.safeString (response['result'], orderIdField); return { 'info': response, 'id': orderId, 'symbol': symbol, 'type': type, 'side': side, 'status': 'open', }; } getOrderIdField () { return 'uuid'; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const orderIdField = this.getOrderIdField (); const request = {}; request[orderIdField] = id; const response = await this.marketGetCancel (this.extend (request, params)); // // { // "success": true, // "message": "''", // "result": { // "uuid": "614c34e4-8d71-11e3-94b5-425861b86ab6" // } // } // const result = this.safeValue (response, 'result', {}); return this.extend (this.parseOrder (result), { 'id': id, 'info': response, 'status': 'canceled', }); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); // https://support.bittrex.com/hc/en-us/articles/115003723911 const request = {}; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } const response = await this.accountGetDeposithistory (this.extend (request, params)); // // { success: true, // message: "", // result: [ { Id: 22578097, // Amount: 0.3, // Currency: "ETH", // Confirmations: 15, // LastUpdated: "2018-06-10T07:12:10.57", // TxId: "0xf50b5ba2ca5438b58f93516eaa523eaf35b4420ca0f24061003df1be7…", // CryptoAddress: "0xb25f281fa51f1635abd4a60b0870a62d2a7fa404" } ] } // // we cannot filter by `since` timestamp, as it isn't set by Bittrex // see https://github.com/ccxt/ccxt/issues/4067 // return this.parseTransactions (response['result'], currency, since, limit); return this.parseTransactions (response['result'], currency, undefined, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); // https://support.bittrex.com/hc/en-us/articles/115003723911 const request = {}; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } const response = await this.accountGetWithdrawalhistory (this.extend (request, params)); // // { // "success" : true, // "message" : "", // "result" : [{ // "PaymentUuid" : "b32c7a5c-90c6-4c6e-835c-e16df12708b1", // "Currency" : "BTC", // "Amount" : 17.00000000, // "Address" : "1DfaaFBdbB5nrHj87x3NHS4onvw1GPNyAu", // "Opened" : "2014-07-09T04:24:47.217", // "Authorized" : true, // "PendingPayment" : false, // "TxCost" : 0.00020000, // "TxId" : null, // "Canceled" : true, // "InvalidAddress" : false // }, { // "PaymentUuid" : "d193da98-788c-4188-a8f9-8ec2c33fdfcf", // "Currency" : "XC", // "Amount" : 7513.75121715, // "Address" : "TcnSMgAd7EonF2Dgc4c9K14L12RBaW5S5J", // "Opened" : "2014-07-08T23:13:31.83", // "Authorized" : true, // "PendingPayment" : false, // "TxCost" : 0.00002000, // "TxId" : "d8a575c2a71c7e56d02ab8e26bb1ef0a2f6cf2094f6ca2116476a569c1e84f6e", // "Canceled" : false, // "InvalidAddress" : false // } // ] // } // return this.parseTransactions (response['result'], currency, since, limit); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // Id: 72578097, // Amount: 0.3, // Currency: "ETH", // Confirmations: 15, // LastUpdated: "2018-06-17T07:12:14.57", // TxId: "0xb31b5ba2ca5438b58f93516eaa523eaf35b4420ca0f24061003df1be7…", // CryptoAddress: "0x2d5f281fa51f1635abd4a60b0870a62d2a7fa404" // } // // fetchWithdrawals // // { // "PaymentUuid" : "e293da98-788c-4188-a8f9-8ec2c33fdfcf", // "Currency" : "XC", // "Amount" : 7513.75121715, // "Address" : "EVnSMgAd7EonF2Dgc4c9K14L12RBaW5S5J", // "Opened" : "2014-07-08T23:13:31.83", // "Authorized" : true, // "PendingPayment" : false, // "TxCost" : 0.00002000, // "TxId" : "b4a575c2a71c7e56d02ab8e26bb1ef0a2f6cf2094f6ca2116476a569c1e84f6e", // "Canceled" : false, // "InvalidAddress" : false // } // const id = this.safeString2 (transaction, 'Id', 'PaymentUuid'); const amount = this.safeFloat (transaction, 'Amount'); const address = this.safeString2 (transaction, 'CryptoAddress', 'Address'); const txid = this.safeString (transaction, 'TxId'); const updated = this.parse8601 (this.safeString (transaction, 'LastUpdated')); const opened = this.parse8601 (this.safeString (transaction, 'Opened')); const timestamp = opened ? opened : updated; const type = (opened === undefined) ? 'deposit' : 'withdrawal'; const currencyId = this.safeString (transaction, 'Currency'); const code = this.safeCurrencyCode (currencyId, currency); let status = 'pending'; if (type === 'deposit') { // // deposits numConfirmations never reach the minConfirmations number // we set all of them to 'ok', otherwise they'd all be 'pending' // // const numConfirmations = this.safeInteger (transaction, 'Confirmations', 0); // const minConfirmations = this.safeInteger (currency['info'], 'MinConfirmation'); // if (numConfirmations >= minConfirmations) { // status = 'ok'; // } // status = 'ok'; } else { const authorized = this.safeValue (transaction, 'Authorized', false); const pendingPayment = this.safeValue (transaction, 'PendingPayment', false); const canceled = this.safeValue (transaction, 'Canceled', false); const invalidAddress = this.safeValue (transaction, 'InvalidAddress', false); if (invalidAddress) { status = 'failed'; } else if (canceled) { status = 'canceled'; } else if (pendingPayment) { status = 'pending'; } else if (authorized && (txid !== undefined)) { status = 'ok'; } } let feeCost = this.safeFloat (transaction, 'TxCost'); if (feeCost === undefined) { if (type === 'deposit') { // according to https://support.bittrex.com/hc/en-us/articles/115000199651-What-fees-does-Bittrex-charge- feeCost = 0; // FIXME: remove hardcoded value that may change any time } } return { 'info': transaction, 'id': id, 'currency': code, 'amount': amount, 'address': address, 'tag': undefined, 'status': status, 'type': type, 'updated': updated, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': { 'currency': code, 'cost': feeCost, }, }; } parseSymbol (id) { const [ quoteId, baseId ] = id.split (this.options['symbolSeparator']); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); return base + '/' + quote; } parseOrder (order, market = undefined) { if ('marketSymbol' in order) { return this.parseOrderV3 (order, market); } else { return this.parseOrderV2 (order, market); } } parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { if (this.options['fetchClosedOrdersFilterBySince']) { return super.parseOrders (orders, market, since, limit, params); } else { return super.parseOrders (orders, market, undefined, limit, params); } } parseOrderStatus (status) { const statuses = { 'CLOSED': 'closed', 'OPEN': 'open', 'CANCELLED': 'canceled', 'CANCELED': 'canceled', }; return this.safeString (statuses, status, status); } parseOrderV3 (order, market = undefined) { // // { // id: '1be35109-b763-44ce-b6ea-05b6b0735c0c', // marketSymbol: 'LTC-ETH', // direction: 'BUY', // type: 'LIMIT', // quantity: '0.50000000', // limit: '0.17846699', // timeInForce: 'GOOD_TIL_CANCELLED', // fillQuantity: '0.50000000', // commission: '0.00022286', // proceeds: '0.08914915', // status: 'CLOSED', // createdAt: '2018-06-23T13:14:28.613Z', // updatedAt: '2018-06-23T13:14:30.19Z', // closedAt: '2018-06-23T13:14:30.19Z' // } // const marketSymbol = this.safeString (order, 'marketSymbol'); let symbol = undefined; let feeCurrency = undefined; if (marketSymbol !== undefined) { const [ baseId, quoteId ] = marketSymbol.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; feeCurrency = quote; } const direction = this.safeStringLower (order, 'direction'); const createdAt = this.safeString (order, 'createdAt'); const updatedAt = this.safeString (order, 'updatedAt'); const closedAt = this.safeString (order, 'closedAt'); let lastTradeTimestamp = undefined; if (closedAt !== undefined) { lastTradeTimestamp = this.parse8601 (closedAt); } else if (updatedAt) { lastTradeTimestamp = this.parse8601 (updatedAt); } const timestamp = this.parse8601 (createdAt); const type = this.safeStringLower (order, 'type'); const quantity = this.safeFloat (order, 'quantity'); const limit = this.safeFloat (order, 'limit'); const fillQuantity = this.safeFloat (order, 'fillQuantity'); const commission = this.safeFloat (order, 'commission'); const proceeds = this.safeFloat (order, 'proceeds'); const status = this.safeStringLower (order, 'status'); let average = undefined; let remaining = undefined; if (fillQuantity !== undefined) { if (proceeds !== undefined) { if (fillQuantity > 0) { average = proceeds / fillQuantity; } else if (proceeds === 0) { average = 0; } } if (quantity !== undefined) { remaining = quantity - fillQuantity; } } return { 'id': this.safeString (order, 'id'), 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': direction, 'price': limit, 'cost': proceeds, 'average': average, 'amount': quantity, 'filled': fillQuantity, 'remaining': remaining, 'status': status, 'fee': { 'cost': commission, 'currency': feeCurrency, }, 'info': order, 'trades': undefined, }; } parseOrderV2 (order, market = undefined) { // // { // "Uuid": "string (uuid)", // "OrderUuid": "8925d746-bc9f-4684-b1aa-e507467aaa99", // "Exchange": "BTC-LTC", // "OrderType": "string", // "Quantity": 100000, // "QuantityRemaining": 100000, // "Limit": 1e-8, // "CommissionPaid": 0, // "Price": 0, // "PricePerUnit": null, // "Opened": "2014-07-09T03:55:48.583", // "Closed": null, // "CancelInitiated": "boolean", // "ImmediateOrCancel": "boolean", // "IsConditional": "boolean" // } // let side = this.safeString2 (order, 'OrderType', 'Type'); const isBuyOrder = (side === 'LIMIT_BUY') || (side === 'BUY'); const isSellOrder = (side === 'LIMIT_SELL') || (side === 'SELL'); if (isBuyOrder) { side = 'buy'; } if (isSellOrder) { side = 'sell'; } // We parse different fields in a very specific order. // Order might well be closed and then canceled. let status = undefined; if (('Opened' in order) && order['Opened']) { status = 'open'; } if (('Closed' in order) && order['Closed']) { status = 'closed'; } if (('CancelInitiated' in order) && order['CancelInitiated']) { status = 'canceled'; } if (('Status' in order) && this.options['parseOrderStatus']) { status = this.parseOrderStatus (this.safeString (order, 'Status')); } let symbol = undefined; if ('Exchange' in order) { const marketId = this.safeString (order, 'Exchange'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = this.parseSymbol (marketId); } } } else { if (market !== undefined) { symbol = market['symbol']; } } let timestamp = undefined; const opened = this.safeString (order, 'Opened'); if (opened !== undefined) { timestamp = this.parse8601 (opened + '+00:00'); } const created = this.safeString (order, 'Created'); if (created !== undefined) { timestamp = this.parse8601 (created + '+00:00'); } let lastTradeTimestamp = undefined; const lastTimestamp = this.safeString (order, 'TimeStamp'); if (lastTimestamp !== undefined) { lastTradeTimestamp = this.parse8601 (lastTimestamp + '+00:00'); } const closed = this.safeString (order, 'Closed'); if (closed !== undefined) { lastTradeTimestamp = this.parse8601 (closed + '+00:00'); } if (timestamp === undefined) { timestamp = lastTradeTimestamp; } let fee = undefined; const feeCost = this.safeFloat2 (order, 'Commission', 'CommissionPaid'); if (feeCost !== undefined) { fee = { 'cost': feeCost, }; if (market !== undefined) { fee['currency'] = market['quote']; } else if (symbol !== undefined) { const currencyIds = symbol.split ('/'); const quoteCurrencyId = currencyIds[1]; fee['currency'] = this.safeCurrencyCode (quoteCurrencyId); } } let price = this.safeFloat (order, 'Limit'); let cost = this.safeFloat (order, 'Price'); const amount = this.safeFloat (order, 'Quantity'); const remaining = this.safeFloat (order, 'QuantityRemaining'); let filled = undefined; if (amount !== undefined && remaining !== undefined) { filled = amount - remaining; if ((status === 'closed') && (remaining > 0)) { status = 'canceled'; } } if (!cost) { if (price && filled) { cost = price * filled; } } if (!price) { if (cost && filled) { price = cost / filled; } } const average = this.safeFloat (order, 'PricePerUnit'); const id = this.safeString2 (order, 'OrderUuid', 'OrderId'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); let response = undefined; try { const orderIdField = this.getOrderIdField (); const request = {}; request[orderIdField] = id; response = await this.accountGetOrder (this.extend (request, params)); } catch (e) { if (this.last_json_response) { const message = this.safeString (this.last_json_response, 'message'); if (message === 'UUID_INVALID') { throw new OrderNotFound (this.id + ' fetchOrder() error: ' + this.last_http_response); } } throw e; } if (!response['result']) { throw new OrderNotFound (this.id + ' order ' + id + ' not found'); } return this.parseOrder (response['result']); } orderToTrade (order) { // this entire method should be moved to the base class const timestamp = this.safeInteger2 (order, 'lastTradeTimestamp', 'timestamp'); return { 'id': this.safeString (order, 'id'), 'side': this.safeString (order, 'side'), 'order': this.safeString (order, 'id'), 'type': this.safeString (order, 'type'), 'price': this.safeFloat (order, 'average'), 'amount': this.safeFloat (order, 'filled'), 'cost': this.safeFloat (order, 'cost'), 'symbol': this.safeString (order, 'symbol'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': this.safeValue (order, 'fee'), 'info': order, 'takerOrMaker': undefined, }; } ordersToTrades (orders) { // this entire method should be moved to the base class const result = []; for (let i = 0; i < orders.length; i++) { result.push (this.orderToTrade (orders[i])); } return result; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchClosedOrders (symbol, since, limit, params); return this.ordersToTrades (orders); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const method = this.safeString (this.options, 'fetchClosedOrdersMethod', 'fetch_closed_orders_v3'); return await this[method] (symbol, since, limit, params); } async fetchClosedOrdersV2 (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const response = await this.accountGetOrderhistory (this.extend (request, params)); const result = this.safeValue (response, 'result', []); const orders = this.parseOrders (result, market, since, limit); if (symbol !== undefined) { return this.filterBySymbol (orders, symbol); } return orders; } async fetchClosedOrdersV3 (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (limit !== undefined) { request['pageSize'] = limit; } if (since !== undefined) { request['startDate'] = this.ymdhms (since, 'T') + 'Z'; } let market = undefined; if (symbol !== undefined) { market = this.market (symbol); // because of this line we will have to rethink the entire v3 // in other words, markets define all the rest of the API // and v3 market ids are reversed in comparison to v2 // v3 has to be a completely separate implementation // otherwise we will have to shuffle symbols and currencies everywhere // which is prone to errors, as was shown here // https://github.com/ccxt/ccxt/pull/5219#issuecomment-499646209 request['marketSymbol'] = market['base'] + '-' + market['quote']; } const response = await this.v3GetOrdersClosed (this.extend (request, params)); const orders = this.parseOrders (response, market, since, limit); if (symbol !== undefined) { return this.filterBySymbol (orders, symbol); } return orders; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.accountGetDepositaddress (this.extend (request, params)); // // { "success": false, "message": "ADDRESS_GENERATING", "result": null } // // { success: true, // message: "", // result: { Currency: "INCNT", // Address: "3PHvQt9bK21f7eVQVdJzrNPcsMzXabEA5Ha" } } } // let address = this.safeString (response['result'], 'Address'); const message = this.safeString (response, 'message'); if (!address || message === 'ADDRESS_GENERATING') { throw new AddressPending (this.id + ' the address for ' + code + ' is being generated (pending, not ready yet, retry again later)'); } let tag = undefined; if (currency['type'] in this.options['tag']) { tag = address; address = currency['address']; } this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'quantity': amount, 'address': address, }; if (tag !== undefined) { request['paymentid'] = tag; } const response = await this.accountGetWithdraw (this.extend (request, params)); const result = this.safeValue (response, 'result', {}); const id = this.safeString (result, 'uuid'); return { 'info': response, 'id': id, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname, }) + '/'; if (api !== 'v2' && api !== 'v3' && api !== 'v3public') { url += this.version + '/'; } if (api === 'public') { url += api + '/' + method.toLowerCase () + path; if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else if (api === 'v3public') { url += path; if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else if (api === 'v2') { url += path; if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else if (api === 'v3') { url += path; let hashString = ''; if (method === 'POST') { body = this.json (params); hashString = body; } else { if (Object.keys (params).length) { url += '?' + this.rawencode (params); } } const contentHash = this.hash (this.encode (hashString), 'sha512', 'hex'); const timestamp = this.milliseconds ().toString (); let auth = timestamp + url + method + contentHash; const subaccountId = this.safeValue (this.options, 'subaccountId'); if (subaccountId !== undefined) { auth += subaccountId; } const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); headers = { 'Api-Key': this.apiKey, 'Api-Timestamp': timestamp, 'Api-Content-Hash': contentHash, 'Api-Signature': signature, }; if (subaccountId !== undefined) { headers['Api-Subaccount-Id'] = subaccountId; } if (method === 'POST') { headers['Content-Type'] = 'application/json'; } } else { this.checkRequiredCredentials (); url += api + '/'; if (((api === 'account') && (path !== 'withdraw')) || (path === 'openorders')) { url += method.toLowerCase (); } const request = { 'apikey': this.apiKey, }; const disableNonce = this.safeValue (this.options, 'disableNonce'); if ((disableNonce === undefined) || !disableNonce) { request['nonce'] = this.nonce (); } url += path + '?' + this.urlencode (this.extend (request, params)); const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512'); headers = { 'apisign': signature }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // // { success: false, message: "message" } // if (body[0] === '{') { const feedback = this.id + ' ' + body; let success = this.safeValue (response, 'success'); if (success === undefined) { const code = this.safeString (response, 'code'); if (code !== undefined) { this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); if (code !== undefined) { this.throwBroadlyMatchedException (this.exceptions['broad'], code, feedback); } } // throw new ExchangeError (this.id + ' malformed response ' + this.json (response)); return; } if (typeof success === 'string') { // bleutrade uses string instead of boolean success = (success === 'true') ? true : false; } if (!success) { const message = this.safeString (response, 'message'); if (message === 'APIKEY_INVALID') { if (this.options['hasAlreadyAuthenticatedSuccessfully']) { throw new DDoSProtection (feedback); } else { throw new AuthenticationError (feedback); } } // https://github.com/ccxt/ccxt/issues/4932 // the following two lines are now redundant, see line 171 in describe() // // if (message === 'DUST_TRADE_DISALLOWED_MIN_VALUE_50K_SAT') // throw new InvalidOrder (this.id + ' order cost should be over 50k satoshi ' + this.json (response)); // if (message === 'INVALID_ORDER') { // Bittrex will return an ambiguous INVALID_ORDER message // upon canceling already-canceled and closed orders // therefore this special case for cancelOrder // let url = 'https://bittrex.com/api/v1.1/market/cancel?apikey=API_KEY&uuid=ORDER_UUID' const cancel = 'cancel'; const indexOfCancel = url.indexOf (cancel); if (indexOfCancel >= 0) { const urlParts = url.split ('?'); const numParts = urlParts.length; if (numParts > 1) { const query = urlParts[1]; const params = query.split ('&'); const numParams = params.length; let orderId = undefined; for (let i = 0; i < numParams; i++) { const param = params[i]; const keyValue = param.split ('='); if (keyValue[0] === 'uuid') { orderId = keyValue[1]; break; } } if (orderId !== undefined) { throw new OrderNotFound (this.id + ' cancelOrder ' + orderId + ' ' + this.json (response)); } else { throw new OrderNotFound (this.id + ' cancelOrder ' + this.json (response)); } } } } this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); if (message !== undefined) { this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); } throw new ExchangeError (feedback); } } } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); // a workaround for APIKEY_INVALID if ((api === 'account') || (api === 'market')) { this.options['hasAlreadyAuthenticatedSuccessfully'] = true; } return response; } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],44:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, PermissionDenied, InvalidOrder, AuthenticationError, InsufficientFunds, OrderNotFound, DDoSProtection } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bitz extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitz', 'name': 'Bit-Z', 'countries': [ 'HK' ], 'rateLimit': 2000, 'version': 'v2', 'userAgent': this.userAgents['chrome'], 'has': { 'fetchTickers': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchOrders': true, 'fetchOrder': true, 'createMarketOrder': false, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTransactions': false, }, 'timeframes': { '1m': '1min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '60min', '4h': '4hour', '1d': '1day', '5d': '5day', '1w': '1week', '1M': '1mon', }, 'hostname': 'apiv2.bitz.com', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/35862606-4f554f14-0b5d-11e8-957d-35058c504b6f.jpg', 'api': { 'market': 'https://{hostname}', 'trade': 'https://{hostname}', 'assets': 'https://{hostname}', }, 'www': 'https://www.bitz.com', 'doc': 'https://apidoc.bitz.com/en/', 'fees': 'https://www.bitz.com/fee?type=1', 'referral': 'https://u.bitz.com/register?invite_code=1429193', }, 'api': { 'market': { 'get': [ 'ticker', 'depth', 'order', // trades 'tickerall', 'kline', 'symbolList', 'currencyRate', 'currencyCoinRate', 'coinRate', ], }, 'trade': { 'post': [ 'addEntrustSheet', 'cancelEntrustSheet', 'cancelAllEntrustSheet', 'getUserHistoryEntrustSheet', // closed orders 'getUserNowEntrustSheet', // open orders 'getEntrustSheetInfo', // order 'depositOrWithdraw', // transactions ], }, 'assets': { 'post': [ 'getUserAssets', ], }, }, 'fees': { 'trading': { 'maker': 0.002, 'taker': 0.002, }, 'funding': { 'withdraw': { 'BTC': '0.5%', 'DKKT': '0.5%', 'ETH': 0.01, 'USDT': '0.5%', 'LTC': '0.5%', 'FCT': '0.5%', 'LSK': '0.5%', 'HXI': '0.8%', 'ZEC': '0.5%', 'DOGE': '0.5%', 'MZC': '0.5%', 'ETC': '0.5%', 'GXS': '0.5%', 'XPM': '0.5%', 'PPC': '0.5%', 'BLK': '0.5%', 'XAS': '0.5%', 'HSR': '0.5%', 'NULS': 5.0, 'VOISE': 350.0, 'PAY': 1.5, 'EOS': 0.6, 'YBCT': 35.0, 'OMG': 0.3, 'OTN': 0.4, 'BTX': '0.5%', 'QTUM': '0.5%', 'DASH': '0.5%', 'GAME': '0.5%', 'BCH': '0.5%', 'GNT': 9.0, 'SSS': 1500.0, 'ARK': '0.5%', 'PART': '0.5%', 'LEO': '0.5%', 'DGB': '0.5%', 'ZSC': 130.0, 'VIU': 350.0, 'BTG': '0.5%', 'ARN': 10.0, 'VTC': '0.5%', 'BCD': '0.5%', 'TRX': 200.0, 'HWC': '0.5%', 'UNIT': '0.5%', 'OXY': '0.5%', 'MCO': 0.3500, 'SBTC': '0.5%', 'BCX': '0.5%', 'ETF': '0.5%', 'PYLNT': 0.4000, 'XRB': '0.5%', 'ETP': '0.5%', }, }, }, 'precision': { 'amount': 8, 'price': 8, }, 'options': { 'fetchOHLCVVolume': true, 'fetchOHLCVWarning': true, 'lastNonceTimestamp': 0, }, 'commonCurrencies': { // https://github.com/ccxt/ccxt/issues/3881 // https://support.bit-z.pro/hc/en-us/articles/360007500654-BOX-BOX-Token- 'BOX': 'BOX Token', 'LEO': 'LeoCoin', 'XRB': 'NANO', 'PXC': 'Pixiecoin', 'VTC': 'VoteCoin', 'TTC': 'TimesChain', }, 'exceptions': { // '200': Success '-102': ExchangeError, // Invalid parameter '-103': AuthenticationError, // Verification failed '-104': ExchangeNotAvailable, // Network Error-1 '-105': AuthenticationError, // Invalid api signature '-106': ExchangeNotAvailable, // Network Error-2 '-109': AuthenticationError, // Invalid scretKey '-110': DDoSProtection, // The number of access requests exceeded '-111': PermissionDenied, // Current IP is not in the range of trusted IP '-112': ExchangeNotAvailable, // Service is under maintenance '-100015': AuthenticationError, // Trade password error '-100044': ExchangeError, // Fail to request data '-100101': ExchangeError, // Invalid symbol '-100201': ExchangeError, // Invalid symbol '-100301': ExchangeError, // Invalid symbol '-100401': ExchangeError, // Invalid symbol '-100302': ExchangeError, // Type of K-line error '-100303': ExchangeError, // Size of K-line error '-200003': AuthenticationError, // Please set trade password '-200005': PermissionDenied, // This account can not trade '-200025': ExchangeNotAvailable, // Temporary trading halt '-200027': InvalidOrder, // Price Error '-200028': InvalidOrder, // Amount must be greater than 0 '-200029': InvalidOrder, // Number must be between %s and %d '-200030': InvalidOrder, // Over price range '-200031': InsufficientFunds, // Insufficient assets '-200032': ExchangeError, // System error. Please contact customer service '-200033': ExchangeError, // Fail to trade '-200034': OrderNotFound, // The order does not exist '-200035': OrderNotFound, // Cancellation error, order filled '-200037': InvalidOrder, // Trade direction error '-200038': ExchangeError, // Trading Market Error '-200055': OrderNotFound, // Order record does not exist '-300069': AuthenticationError, // api_key is illegal '-300101': ExchangeError, // Transaction type error '-300102': InvalidOrder, // Price or number cannot be less than 0 '-300103': AuthenticationError, // Trade password error '-301001': ExchangeNotAvailable, // Network Error-3 }, }); } async fetchMarkets (params = {}) { const response = await this.marketGetSymbolList (params); // // { status: 200, // msg: "", // data: { ltc_btc: { id: "1", // name: "ltc_btc", // coinFrom: "ltc", // coinTo: "btc", // numberFloat: "4", // priceFloat: "8", // status: "1", // minTrade: "0.010", // maxTrade: "500000000.000" }, // qtum_usdt: { id: "196", // name: "qtum_usdt", // coinFrom: "qtum", // coinTo: "usdt", // numberFloat: "4", // priceFloat: "2", // status: "1", // minTrade: "0.100", // maxTrade: "500000000.000" }, }, // time: 1535969146, // microtime: "0.66955600 1535969146", // source: "api" } // const markets = this.safeValue (response, 'data'); const ids = Object.keys (markets); const result = []; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = markets[id]; const numericId = this.safeString (market, 'id'); const baseId = this.safeString (market, 'coinFrom'); const quoteId = this.safeString (market, 'coinTo'); let base = baseId.toUpperCase (); let quote = quoteId.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'numberFloat'), 'price': this.safeInteger (market, 'priceFloat'), }; result.push ({ 'info': market, 'id': id, 'numericId': numericId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minTrade'), 'max': this.safeFloat (market, 'maxTrade'), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.assetsPostGetUserAssets (params); // // { // status: 200, // msg: "", // data: { // cny: 0, // usd: 0, // btc_total: 0, // info: [{ // "name": "zpr", // "num": "37.49067275", // "over": "37.49067275", // "lock": "0.00000000", // "btc": "0.00000000", // "usd": "0.00000000", // "cny": "0.00000000", // }], // }, // time: 1535983966, // microtime: "0.70400500 1535983966", // source: "api", // } // const balances = this.safeValue (response['data'], 'info'); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'name'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['used'] = this.safeFloat (balance, 'lock'); account['total'] = this.safeFloat (balance, 'num'); account['free'] = this.safeFloat (balance, 'over'); result[code] = account; } return this.parseBalance (result); } parseTicker (ticker, market = undefined) { // // { symbol: "eth_btc", // quoteVolume: "3905.72", // volume: "97058.21", // priceChange: "-1.72", // priceChange24h: "-1.65", // askPrice: "0.03971272", // askQty: "0.0663", // bidPrice: "0.03961469", // bidQty: "19.5451", // open: "0.04036769", // high: "0.04062988", // low: "0.03956123", // now: "0.03970100", // firstId: 115567767, // lastId: 115795316, // dealCount: 14078, // numberPrecision: 4, // pricePrecision: 8, // cny: "1959.05", // usd: "287.10", // krw: "318655.82" } // const timestamp = undefined; let symbol = undefined; if (market === undefined) { const marketId = this.safeString (ticker, 'symbol'); market = this.safeValue (this.markets_by_id, marketId); } if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'now'); const open = this.safeFloat (ticker, 'open'); let change = undefined; let average = undefined; if (last !== undefined && open !== undefined) { change = last - open; average = this.sum (last, open) / 2; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bidPrice'), 'bidVolume': this.safeFloat (ticker, 'bidQty'), 'ask': this.safeFloat (ticker, 'askPrice'), 'askVolume': this.safeFloat (ticker, 'askQty'), 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': this.safeFloat (ticker, 'priceChange24h'), 'average': average, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), 'info': ticker, }; } parseMicrotime (microtime) { if (microtime === undefined) { return microtime; } const parts = microtime.split (' '); const milliseconds = parseFloat (parts[0]); const seconds = parseInt (parts[1]); const total = this.sum (seconds, milliseconds); return parseInt (total * 1000); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.marketGetTicker (this.extend (request, params)); // // { status: 200, // msg: "", // data: { symbol: "eth_btc", // quoteVolume: "3905.72", // volume: "97058.21", // priceChange: "-1.72", // priceChange24h: "-1.65", // askPrice: "0.03971272", // askQty: "0.0663", // bidPrice: "0.03961469", // bidQty: "19.5451", // open: "0.04036769", // high: "0.04062988", // low: "0.03956123", // now: "0.03970100", // firstId: 115567767, // lastId: 115795316, // dealCount: 14078, // numberPrecision: 4, // pricePrecision: 8, // cny: "1959.05", // usd: "287.10", // krw: "318655.82" }, // time: 1535970397, // microtime: "0.76341900 1535970397", // source: "api" } // const ticker = this.parseTicker (response['data'], market); const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); return this.extend (ticker, { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), }); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbols !== undefined) { const ids = this.marketIds (symbols); request['symbols'] = ids.join (','); } const response = await this.marketGetTickerall (this.extend (request, params)); // // { status: 200, // msg: "", // data: { ela_btc: { symbol: "ela_btc", // quoteVolume: "0.00", // volume: "3.28", // priceChange: "0.00", // priceChange24h: "0.00", // askPrice: "0.00147984", // askQty: "5.4580", // bidPrice: "0.00120230", // bidQty: "12.5384", // open: "0.00149078", // high: "0.00149078", // low: "0.00149078", // now: "0.00149078", // firstId: 115581219, // lastId: 115581219, // dealCount: 1, // numberPrecision: 4, // pricePrecision: 8, // cny: "73.66", // usd: "10.79", // krw: "11995.03" } }, // time: 1535971578, // microtime: "0.39854200 1535971578", // source: "api" } // const tickers = this.safeValue (response, 'data'); const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); const result = {}; const ids = Object.keys (tickers); for (let i = 0; i < ids.length; i++) { const id = ids[i]; let ticker = tickers[id]; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; } ticker = this.parseTicker (tickers[id], market); let symbol = ticker['symbol']; if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } else { const [ baseId, quoteId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol !== undefined) { result[symbol] = this.extend (ticker, { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), }); } } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; const response = await this.marketGetDepth (this.extend (request, params)); // // { status: 200, // msg: "", // data: { asks: [ ["10.00000000", "0.4426", "4.4260"], // ["1.00000000", "0.8339", "0.8339"], // ["0.91700000", "0.0500", "0.0458"], // ["0.20000000", "0.1000", "0.0200"], // ["0.03987120", "16.1262", "0.6429"], // ["0.03986120", "9.7523", "0.3887"] ], // bids: [ ["0.03976145", "0.0359", "0.0014"], // ["0.03973401", "20.9493", "0.8323"], // ["0.03967970", "0.0328", "0.0013"], // ["0.00000002", "10000.0000", "0.0002"], // ["0.00000001", "231840.7500", "0.0023"] ], // coinPair: "eth_btc" }, // time: 1535974778, // microtime: "0.04017400 1535974778", // source: "api" } // const orderbook = this.safeValue (response, 'data'); const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); return this.parseOrderBook (orderbook, timestamp); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { id: 115807453, // t: "19:36:24", // T: 1535974584, // p: "0.03983296", // n: "0.1000", // s: "buy" }, // const id = this.safeString (trade, 'id'); const timestamp = this.safeTimestamp (trade, 'T'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const price = this.safeFloat (trade, 'p'); const amount = this.safeFloat (trade, 'n'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = this.priceToPrecision (symbol, amount * price); } } const side = this.safeString (trade, 's'); return { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': undefined, 'type': 'limit', 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.marketGetOrder (this.extend (request, params)); // // { status: 200, // msg: "", // data: [ { id: 115807453, // t: "19:36:24", // T: 1535974584, // p: "0.03983296", // n: "0.1000", // s: "buy" }, // { id: 115806811, // t: "19:33:19", // T: 1535974399, // p: "0.03981135", // n: "9.4612", // s: "sell" } ], // time: 1535974583, // microtime: "0.57118100 1535974583", // source: "api" } // return this.parseTrades (response['data'], market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // { time: "1535973420000", // open: "0.03975084", // high: "0.03975084", // low: "0.03967700", // close: "0.03967700", // volume: "12.4733", // datetime: "2018-09-03 19:17:00" } // return [ this.safeInteger (ohlcv, 'time'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const duration = this.parseTimeframe (timeframe) * 1000; const market = this.market (symbol); const request = { 'symbol': market['id'], 'resolution': this.timeframes[timeframe], }; if (limit !== undefined) { request['size'] = Math.min (limit, 300); // 1-300 if (since !== undefined) { request['to'] = this.sum (since, limit * duration * 1000); } } else { if (since !== undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a limit argument if the since argument is specified'); } } const response = await this.marketGetKline (this.extend (request, params)); // // { status: 200, // msg: "", // data: { bars: [ { time: "1535973420000", // open: "0.03975084", // high: "0.03975084", // low: "0.03967700", // close: "0.03967700", // volume: "12.4733", // datetime: "2018-09-03 19:17:00" }, // { time: "1535955480000", // open: "0.04009900", // high: "0.04016745", // low: "0.04009900", // close: "0.04012074", // volume: "74.4803", // datetime: "2018-09-03 14:18:00" } ], // resolution: "1min", // symbol: "eth_btc", // from: "1535973420000", // to: "1535955480000", // size: 300 }, // time: 1535973435, // microtime: "0.56462100 1535973435", // source: "api" } // const bars = this.safeValue (response['data'], 'bars', undefined); if (bars === undefined) { return []; } return this.parseOHLCVs (bars, market, timeframe, since, limit); } parseOrderStatus (status) { const statuses = { '0': 'open', '1': 'open', // partially filled '2': 'closed', // filled '3': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "id": "693248739", // order id // "uId": "2074056", // uid // "price": "100", // price // "number": "10", // number // "numberOver": "10", // undealed // "flag": "sale", // flag // "status": "0", // unfilled // "coinFrom": "vtc", // "coinTo": "dkkt", // "numberDeal": "0" // dealed // } // const id = this.safeString (order, 'id'); let symbol = undefined; if (market === undefined) { const baseId = this.safeString (order, 'coinFrom'); const quoteId = this.safeString (order, 'coinTo'); if ((baseId !== undefined) && (quoteId !== undefined)) { const marketId = baseId + '_' + quoteId; if (marketId in this.markets_by_id) { market = this.safeValue (this.markets_by_id, marketId); } else { const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } } if (market !== undefined) { symbol = market['symbol']; } let side = this.safeString (order, 'flag'); if (side !== undefined) { side = (side === 'sale') ? 'sell' : 'buy'; } const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'number'); const remaining = this.safeFloat (order, 'numberOver'); const filled = this.safeFloat (order, 'numberDeal'); let timestamp = this.safeInteger (order, 'timestamp'); if (timestamp === undefined) { timestamp = this.safeTimestamp (order, 'created'); } let cost = this.safeFloat (order, 'orderTotalPrice'); if (price !== undefined) { if (filled !== undefined) { cost = filled * price; } } const status = this.parseOrderStatus (this.safeString (order, 'status')); return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': undefined, 'info': order, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type !== 'limit') { throw new ExchangeError (this.id + ' createOrder allows limit orders only'); } const market = this.market (symbol); const orderType = (side === 'buy') ? '1' : '2'; if (!this.password) { throw new ExchangeError (this.id + ' createOrder() requires you to set exchange.password = "YOUR_TRADING_PASSWORD" (a trade password is NOT THE SAME as your login password)'); } const request = { 'symbol': market['id'], 'type': orderType, 'price': this.priceToPrecision (symbol, price), 'number': this.amountToPrecision (symbol, amount), 'tradePwd': this.password, }; const response = await this.tradePostAddEntrustSheet (this.extend (request, params)); // // { // "status": 200, // "msg": "", // "data": { // "id": "693248739", // order id // "uId": "2074056", // uid // "price": "100", // price // "number": "10", // number // "numberOver": "10", // undealed // "flag": "sale", // flag // "status": "0", // unfilled // "coinFrom": "vtc", // "coinTo": "dkkt", // "numberDeal": "0" // dealed // }, // "time": "1533035297", // "microtime": "0.41892000 1533035297", // "source": "api", // } // const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); const order = this.extend ({ 'timestamp': timestamp, }, response['data']); return this.parseOrder (order, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'entrustSheetId': id, }; const response = await this.tradePostCancelEntrustSheet (this.extend (request, params)); // // { // "status":200, // "msg":"", // "data":{ // "updateAssetsData":{ // "coin":"bz", // "over":"1000.00000000", // "lock":"-1000.00000000" // }, // "assetsInfo":{ // "coin":"bz", // "over":"9999.99999999", // "lock":"9999.99999999" // } // }, // "time":"1535464383", // "microtime":"0.91558000 1535464383", // "source":"api" // } // return response; } async cancelOrders (ids, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'ids': ids.join (','), }; const response = await this.tradePostCancelEntrustSheet (this.extend (request, params)); // // { // "status":200, // "msg":"", // "data":{ // "744173808":{ // "updateAssetsData":{ // "coin":"bz", // "over":"100.00000000", // "lock":"-100.00000000" // }, // "assetsInfo":{ // "coin":"bz", // "over":"899.99999999", // "lock":"19099.99999999" // } // }, // "744173809":{ // "updateAssetsData":{ // "coin":"bz", // "over":"100.00000000", // "lock":"-100.00000000" // }, // "assetsInfo":{ // "coin":"bz", // "over":"999.99999999", // "lock":"18999.99999999" // } // } // }, // "time":"1535525649", // "microtime":"0.05009400 1535525649", // "source":"api" // } // return response; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'entrustSheetId': id, }; const response = await this.tradePostGetEntrustSheetInfo (this.extend (request, params)); // // { // "status":200, // "msg":"", // "data":{ // "id":"708279852", // "uId":"2074056", // "price":"100.00000000", // "number":"10.0000", // "total":"0.00000000", // "numberOver":"10.0000", // "numberDeal":"0.0000", // "flag":"sale", // "status":"0", //0:unfilled, 1:partial deal, 2:all transactions, 3:already cancelled // "coinFrom":"bz", // "coinTo":"usdt", // "orderTotalPrice":"0", // "created":"1533279876" // }, // "time":"1533280294", // "microtime":"0.36859200 1533280294", // "source":"api" // } // return this.parseOrder (response['data']); } async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'coinFrom': market['baseId'], 'coinTo': market['quoteId'], // 'type': 1, // optional integer, 1 = buy, 2 = sell // 'page': 1, // optional integer // 'pageSize': 100, // optional integer, max 100 // 'startTime': 1510235730, // optional integer timestamp in seconds // 'endTime': 1510235730, // optional integer timestamp in seconds }; if (limit !== undefined) { request['page'] = 1; request['pageSize'] = limit; } if (since !== undefined) { request['startTime'] = parseInt (since / 1000); // request['endTime'] = parseInt (since / 1000); } const response = await this[method] (this.extend (request, params)); // // { // "status": 200, // "msg": "", // "data": { // "data": [ // { // "id": "693248739", // "uid": "2074056", // "price": "100.00000000", // "number": "10.0000", // "total": "0.00000000", // "numberOver": "0.0000", // "numberDeal": "0.0000", // "flag": "sale", // "status": "3", // 0:unfilled, 1:partial deal, 2:all transactions, 3:already cancelled // "isNew": "N", // "coinFrom": "vtc", // "coinTo": "dkkt", // "created": "1533035300", // }, // { // "id": "723086996", // "uid": "2074056", // "price": "100.00000000", // "number": "10.0000", // "total": "0.00000000", // "numberOver": "0.0000", // "numberDeal": "0.0000", // "flag": "sale", // "status": "3", // "isNew": "N", // "coinFrom": "bz", // "coinTo": "usdt", // "created": "1533523568", // }, // ], // "pageInfo": { // "limit": "10", // "offest": "0", // "current_page": "1", // "page_size": "10", // "total_count": "17", // "page_count": "2", // } // }, // "time": "1533279329", // "microtime": "0.15305300 1533279329", // "source": "api" // } // const orders = this.safeValue (response['data'], 'data', []); return this.parseOrders (orders, undefined, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersWithMethod ('tradePostGetUserHistoryEntrustSheet', symbol, since, limit, params); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersWithMethod ('tradePostGetUserNowEntrustSheet', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersWithMethod ('tradePostGetUserHistoryEntrustSheet', symbol, since, limit, params); } parseTransactionStatus (status) { const statuses = { '1': 'pending', '2': 'pending', '3': 'pending', '4': 'ok', '5': 'canceled', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // { // "id": '96275', // "uid": '2109073', // "wallet": '0xf4c4141c0127bc37b1d0c409a091920eba13ada7', // "txid": '0xb7adfa52aa566f9ac112e3c01f77bd91179b19eab12092a9a5a8b33d5086e31d', // "confirm": '12', // "number": '0.50000000', // "status": 4, // "updated": '1534944168605', // "addressUrl": 'https://etherscan.io/address/', // "txidUrl": 'https://etherscan.io/tx/', // "description": 'Ethereum', // "coin": 'eth', // "memo": '' // } // // { // "id":"397574", // "uid":"2033056", // "wallet":"1AG1gZvQAYu3WBvgg7p4BMMghQD2gE693k", // "txid":"", // "confirm":"0", // "number":"1000.00000000", // "status":1, // "updated":"0", // "addressUrl":"http://omniexplorer.info/lookupadd.aspx?address=", // "txidUrl":"http://omniexplorer.info/lookuptx.aspx?txid=", // "description":"Tether", // "coin":"usdt", // "memo":"" // } // // { // "id":"153606", // "uid":"2033056", // "wallet":"1AG1gZvQAYu3WBvgg7p4BMMghQD2gE693k", // "txid":"aa2b179f84cd6dedafd41845e0fbf7f01e14c0d71ea3140d03d6f5a9ccd93199", // "confirm":"0", // "number":"761.11110000", // "status":4, // "updated":"1536726133579", // "addressUrl":"http://omniexplorer.info/lookupadd.aspx?address=", // "txidUrl":"http://omniexplorer.info/lookuptx.aspx?txid=", // "description":"Tether", // "coin":"usdt", // "memo":"" // } // let timestamp = this.safeInteger (transaction, 'updated'); if (timestamp === 0) { timestamp = undefined; } const currencyId = this.safeString (transaction, 'coin'); const code = this.safeCurrencyCode (currencyId, currency); const type = this.safeStringLower (transaction, 'type'); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); return { 'id': this.safeString (transaction, 'id'), 'txid': this.safeString (transaction, 'txid'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': this.safeString (transaction, 'wallet'), 'tag': this.safeString (transaction, 'memo'), 'type': type, 'amount': this.safeFloat (transaction, 'number'), 'currency': code, 'status': status, 'updated': timestamp, 'fee': undefined, 'info': transaction, }; } parseTransactionsByType (type, transactions, code = undefined, since = undefined, limit = undefined) { const result = []; for (let i = 0; i < transactions.length; i++) { const transaction = this.parseTransaction (this.extend ({ 'type': type, }, transactions[i])); result.push (transaction); } return this.filterByCurrencySinceLimit (result, code, since, limit); } parseTransactionType (type) { const types = { 'deposit': 1, 'withdrawal': 2, }; return this.safeInteger (types, type, type); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchTransactionsForType ('deposit', code, since, limit, params); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchTransactionsForType ('withdrawal', code, since, limit, params); } async fetchTransactionsForType (type, code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency `code` argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'coin': currency['id'], 'type': this.parseTransactionType (type), }; if (since !== undefined) { request['startTime'] = parseInt (since / 1000).toString (); } if (limit !== undefined) { request['page'] = 1; request['pageSize'] = limit; } const response = await this.tradePostDepositOrWithdraw (this.extend (request, params)); const transactions = this.safeValue (response['data'], 'data', []); return this.parseTransactionsByType (type, transactions, code, since, limit); } nonce () { const currentTimestamp = this.seconds (); if (currentTimestamp > this.options['lastNonceTimestamp']) { this.options['lastNonceTimestamp'] = currentTimestamp; this.options['lastNonce'] = 100000; } this.options['lastNonce'] = this.sum (this.options['lastNonce'], 1); return this.options['lastNonce']; } sign (path, api = 'market', method = 'GET', params = {}, headers = undefined, body = undefined) { const baseUrl = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname }); let url = baseUrl + '/' + this.capitalize (api) + '/' + path; let query = undefined; if (api === 'market') { query = this.urlencode (params); if (query.length) { url += '?' + query; } } else { this.checkRequiredCredentials (); body = this.rawencode (this.keysort (this.extend ({ 'apiKey': this.apiKey, 'timeStamp': this.seconds (), 'nonce': this.nonce (), }, params))); body += '&sign=' + this.hash (this.encode (body + this.secret)); headers = { 'Content-type': 'application/x-www-form-urlencoded' }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } const status = this.safeString (response, 'status'); if (status !== undefined) { const feedback = this.id + ' ' + body; // // {"status":-107,"msg":"","data":"","time":1535968848,"microtime":"0.89092200 1535968848","source":"api"} // if (status === '200') { // // {"status":200,"msg":"","data":-200031,"time":1535999806,"microtime":"0.85476800 1535999806","source":"api"} // const code = this.safeInteger (response, 'data'); if (code !== undefined) { this.throwExactlyMatchedException (this.exceptions, code, feedback); throw new ExchangeError (feedback); } else { return; // no error } } this.throwExactlyMatchedException (this.exceptions, status, feedback); throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9}],45:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); // --------------------------------------------------------------------------- module.exports = class bl3p extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bl3p', 'name': 'BL3P', 'countries': [ 'NL', 'EU' ], // Netherlands, EU 'rateLimit': 1000, 'version': '1', 'comment': 'An exchange market by BitonicNL', 'has': { 'CORS': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/28501752-60c21b82-6feb-11e7-818b-055ee6d0e754.jpg', 'api': 'https://api.bl3p.eu', 'www': [ 'https://bl3p.eu', 'https://bitonic.nl', ], 'doc': [ 'https://github.com/BitonicNL/bl3p-api/tree/master/docs', 'https://bl3p.eu/api', 'https://bitonic.nl/en/api', ], }, 'api': { 'public': { 'get': [ '{market}/ticker', '{market}/orderbook', '{market}/trades', ], }, 'private': { 'post': [ '{market}/money/depth/full', '{market}/money/order/add', '{market}/money/order/cancel', '{market}/money/order/result', '{market}/money/orders', '{market}/money/orders/history', '{market}/money/trades/fetch', 'GENMKT/money/info', 'GENMKT/money/deposit_address', 'GENMKT/money/new_deposit_address', 'GENMKT/money/wallet/history', 'GENMKT/money/withdraw', ], }, }, 'markets': { 'BTC/EUR': { 'id': 'BTCEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025 }, 'LTC/EUR': { 'id': 'LTCEUR', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'LTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025 }, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGENMKTMoneyInfo (params); const data = this.safeValue (response, 'data', {}); const wallets = this.safeValue (data, 'wallets'); const result = { 'info': data }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currency = this.currency (code); const currencyId = currency['id']; const wallet = this.safeValue (wallets, currencyId, {}); const available = this.safeValue (wallet, 'available', {}); const balance = this.safeValue (wallet, 'balance', {}); const account = this.account (); account['free'] = this.safeFloat (available, 'value'); account['total'] = this.safeFloat (balance, 'value'); result[code] = account; } return this.parseBalance (result); } parseBidAsk (bidask, priceKey = 0, amountKey = 1) { return [ bidask[priceKey] / 100000.0, bidask[amountKey] / 100000000.0, ]; } async fetchOrderBook (symbol, limit = undefined, params = {}) { const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetMarketOrderbook (this.extend (request, params)); const orderbook = this.safeValue (response, 'data'); return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price_int', 'amount_int'); } async fetchTicker (symbol, params = {}) { const request = { 'market': this.marketId (symbol), }; const ticker = await this.publicGetMarketTicker (this.extend (request, params)); const timestamp = this.safeTimestamp (ticker, 'timestamp'); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker['volume'], '24h'), 'quoteVolume': undefined, 'info': ticker, }; } parseTrade (trade, market = undefined) { const id = this.safeString (trade, 'trade_id'); const timestamp = this.safeInteger (trade, 'date'); let price = this.safeFloat (trade, 'price_int'); if (price !== undefined) { price /= 100000.0; } let amount = this.safeFloat (trade, 'amount_int'); if (amount !== undefined) { amount /= 100000000.0; } let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { const market = this.market (symbol); const response = await this.publicGetMarketTrades (this.extend ({ 'market': market['id'], }, params)); const result = this.parseTrades (response['data']['trades'], market, since, limit); return result; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { const market = this.market (symbol); const order = { 'market': market['id'], 'amount_int': parseInt (amount * 100000000), 'fee_currency': market['quote'], 'type': (side === 'buy') ? 'bid' : 'ask', }; if (type === 'limit') { order['price_int'] = parseInt (price * 100000.0); } const response = await this.privatePostMarketMoneyOrderAdd (this.extend (order, params)); const orderId = this.safeString (response['data'], 'order_id'); return { 'info': response, 'id': orderId, }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'order_id': id, }; return await this.privatePostMarketMoneyOrderCancel (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const request = this.implodeParams (path, params); let url = this.urls['api'] + '/' + this.version + '/' + request; const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); const secret = this.base64ToBinary (this.secret); // eslint-disable-next-line quotes const auth = request + "\0" + body; const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Rest-Key': this.apiKey, 'Rest-Sign': this.decode (signature), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7}],46:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, InvalidOrder, AuthenticationError, InsufficientFunds, BadRequest } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bleutrade extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bleutrade', 'name': 'Bleutrade', 'countries': ['BR'], // Brazil 'rateLimit': 1000, 'certified': false, 'has': { 'CORS': true, 'cancelOrder': false, // todo 'createLimitOrder': false, // todo 'createMarketOrder': false, // todo 'createOrder': false, // todo 'editOrder': false, // todo 'withdraw': false, // todo 'fetchTrades': false, 'fetchTickers': true, 'fetchTicker': true, 'fetchOrders': false, 'fetchClosedOrders': true, 'fetchOpenOrders': true, 'fetchWithdrawals': true, 'fetchOrderTrades': false, 'fetchLedger': true, 'fetchDepositAddress': true, }, 'timeframes': { '15m': '15m', '20m': '20m', '30m': '30m', '1h': '1h', '2h': '2h', '3h': '3h', '4h': '4h', '6h': '6h', '8h': '8h', '12h': '12h', '1d': '1d', }, 'hostname': 'bleutrade.com', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/30303000-b602dbe6-976d-11e7-956d-36c5049c01e7.jpg', 'api': { 'v3Private': 'https://{hostname}/api/v3/private', 'v3Public': 'https://{hostname}/api/v3/public', }, 'www': ['https://bleutrade.com'], 'doc': [ 'https://app.swaggerhub.com/apis-docs/bleu/white-label/3.0.0', ], 'fees': 'https://bleutrade.com/fees/', }, 'api': { 'v3Public': { 'get': [ 'getassets', 'getmarkets', 'getticker', 'getmarketsummary', 'getmarketsummaries', 'getorderbook', 'getmarkethistory', 'getcandles', ], }, 'v3Private': { 'get': [ 'statement', ], 'post': [ 'getbalance', 'getbalances', 'buylimit', 'selllimit', 'buylimitami', 'selllimitami', 'buystoplimit', 'sellstoplimit', 'ordercancel', 'getopenorders', 'getcloseorders', 'getdeposithistory', 'getdepositaddress', 'getmytransactions', 'withdraw', 'directtransfer', 'getwithdrawhistory', 'getlimits', ], }, }, 'commonCurrencies': { 'EPC': 'Epacoin', }, 'exceptions': { 'exact': { 'ERR_INSUFICIENT_BALANCE': InsufficientFunds, 'ERR_LOW_VOLUME': BadRequest, }, 'broad': { 'Order is not open': InvalidOrder, 'Invalid Account / Api KEY / Api Secret': AuthenticationError, // also happens when an invalid nonce is used }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.25 / 100, 'maker': 0.25 / 100, }, }, 'options': { 'parseOrderStatus': true, 'symbolSeparator': '_', }, }); // undocumented api calls // https://bleutrade.com/api/v3/public/tradingview/symbols?symbol=ETH_BTC // https://bleutrade.com/api/v3/public/tradingview/config // https://bleutrade.com/api/v3/public/tradingview/time // https://bleutrade.com/api/v3/private/getcloseorders?market=ETH_BTC // https://bleutrade.com/config contains the fees } async fetchCurrencies (params = {}) { const response = await this.v3PublicGetGetassets (params); const items = response['result']; const result = {}; for (let i = 0; i < items.length; i++) { // { Asset: 'USDT', // AssetLong: 'Tether', // MinConfirmation: 4, // WithdrawTxFee: 1, // WithdrawTxFeePercent: 0, // SystemProtocol: 'ETHERC20', // IsActive: true, // InfoMessage: '', // MaintenanceMode: false, // MaintenanceMessage: '', // FormatPrefix: '', // FormatSufix: '', // DecimalSeparator: '.', // ThousandSeparator: ',', // DecimalPlaces: 8, // Currency: 'USDT', // CurrencyLong: 'Tether', // CoinType: 'ETHERC20' } const item = items[i]; const id = this.safeString (item, 'Asset'); const code = this.safeCurrencyCode (id); result[code] = { 'id': id, 'code': code, 'name': this.safeString (item, 'AssetLong'), 'active': this.safeValue (item, 'IsActive') && !this.safeValue (item, 'MaintenanceMode'), 'fee': this.safeFloat (item, 'WithdrawTxFee'), 'precision': this.safeFloat (item, 'DecimalPlaces'), 'info': item, 'limits': this.limits, }; } return result; } async fetchMarkets (params = {}) { // https://github.com/ccxt/ccxt/issues/5668 const response = await this.v3PublicGetGetmarkets (params); const result = []; const markets = this.safeValue (response, 'result'); for (let i = 0; i < markets.length; i++) { const market = markets[i]; // { MarketName: 'LTC_USDT', // MarketAsset: 'LTC', // BaseAsset: 'USDT', // MarketAssetLong: 'Litecoin', // BaseAssetLong: 'Tether', // IsActive: true, // MinTradeSize: 0.0001, // InfoMessage: '', // MarketCurrency: 'LTC', // BaseCurrency: 'USDT', // MarketCurrencyLong: 'Litecoin', // BaseCurrencyLong: 'Tether' } const id = this.safeString (market, 'MarketName'); const baseId = this.safeString (market, 'MarketAsset'); const quoteId = this.safeString (market, 'BaseAsset'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': 8, 'price': 8, }; const active = this.safeValue (market, 'IsActive', false); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'info': market, 'precision': precision, 'maker': this.fees['trading']['maker'], 'taker': this.fees['trading']['taker'], 'limits': { 'amount': { 'min': this.safeFloat (market, 'MinTradeSize'), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, }, }); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), 'type': 'ALL', }; if (limit !== undefined) { request['depth'] = limit; // 50 } const response = await this.v3PublicGetGetorderbook (this.extend (request, params)); const orderbook = this.safeValue (response, 'result'); if (!orderbook) { throw new ExchangeError (this.id + ' no orderbook data in ' + this.json (response)); } return this.parseOrderBook (orderbook, undefined, 'buy', 'sell', 'Rate', 'Quantity'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.v3PublicGetGetmarketsummary (this.extend (request, params)); const ticker = response['result'][0]; return this.parseTicker (ticker, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.v3PublicGetGetmarketsummaries (params); const result = this.safeValue (response, 'result'); const tickers = []; for (let i = 0; i < result.length; i++) { const ticker = this.parseTicker (result[i]); tickers.push (ticker); } return this.filterByArray (tickers, 'symbol', symbols); } parseTicker (ticker, market = undefined) { // { TimeStamp: '2020-01-14 14:32:28', // MarketName: 'LTC_USDT', // MarketAsset: 'LTC', // BaseAsset: 'USDT', // MarketAssetName: 'Litecoin', // BaseAssetName: 'Tether', // PrevDay: 49.2867503, // High: 56.78622664, // Low: 49.27384025, // Last: 53.94, // Average: 51.37509368, // Volume: 1.51282404, // BaseVolume: 77.72147677, // Bid: 53.62070218, // Ask: 53.94, // IsActive: 'true', // InfoMessage: '', // MarketCurrency: 'Litecoin', // BaseCurrency: 'Tether' } const timestamp = this.parse8601 (this.safeString (ticker, 'TimeStamp')); let symbol = undefined; const marketId = this.safeString (ticker, 'MarketName'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { symbol = this.parseSymbol (marketId); } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const previous = this.safeFloat (ticker, 'PrevDay'); const last = this.safeFloat (ticker, 'Last'); let change = undefined; let percentage = undefined; if (last !== undefined) { if (previous !== undefined) { change = last - previous; if (previous > 0) { percentage = (change / previous) * 100; } } } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'High'), 'low': this.safeFloat (ticker, 'Low'), 'bid': this.safeFloat (ticker, 'Bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'Ask'), 'askVolume': undefined, 'vwap': undefined, 'open': previous, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'Volume'), 'quoteVolume': this.safeFloat (ticker, 'BaseVolume'), 'info': ticker, }; } parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { const timestamp = this.parse8601 (ohlcv['TimeStamp'] + '+00:00'); return [ timestamp, this.safeFloat (ohlcv, 'Open'), this.safeFloat (ohlcv, 'High'), this.safeFloat (ohlcv, 'Low'), this.safeFloat (ohlcv, 'Close'), this.safeFloat (ohlcv, 'Volume'), ]; } async fetchOHLCV (symbol, timeframe = '15m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'period': this.timeframes[timeframe], 'market': market['id'], 'count': limit, }; const response = await this.v3PublicGetGetcandles (this.extend (request, params)); return this.parseOHLCVs (response['result'], market, timeframe, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type !== 'limit') { // todo: STOP-LIMIT and AMI order types are supported throw new InvalidOrder (this.id + ' allows limit orders only'); } await this.loadMarkets (); const request = { 'rate': this.priceToPrecision (symbol, price), 'quantity': this.amountToPrecision (symbol, amount), 'tradeType': (side === 'buy') ? '1' : '0', 'market': this.marketId (symbol), }; let response = undefined; if (side === 'buy') { response = await this.v3PrivatePostBuylimit (this.extend (request, params)); } else { response = await this.v3PrivatePostSelllimit (this.extend (request, params)); } // { success: true, // message: "", // result: "161105236" }, return { 'info': response, 'id': this.safeString (response, 'result'), }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'orderid': id, }; const response = await this.v3PrivatePostOrdercancel (this.extend (request, params)); // { success: true, message: '', result: '' } return response; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const response = await this.v3PrivatePostGetopenorders (this.extend (request, params)); const items = this.safeValue (response, 'result', []); return this.parseOrders (items, market, since, limit); } parseSymbol (id) { let [ base, quote ] = id.split (this.options['symbolSeparator']); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); return base + '/' + quote; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.v3PrivatePostGetbalances (params); const result = { 'info': response }; const items = response['result']; for (let i = 0; i < items.length; i++) { const item = items[i]; const currencyId = this.safeString (item, 'Asset'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (item, 'Available'); account['total'] = this.safeFloat (item, 'Balance'); result[code] = account; } return this.parseBalance (result); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const response = await this.v3PrivatePostGetcloseorders (this.extend (request, params)); const orders = this.safeValue (response, 'result', []); return this.parseOrders (orders, market, since, limit); } async fetchTransactionsWithMethod (method, code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this[method] (params); const transactions = this.safeValue (response, 'result', []); return this.parseTransactions (transactions, code, since, limit); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchTransactionsWithMethod ('v3PrivatePostGetdeposithistory', code, since, limit, params); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchTransactionsWithMethod ('v3PrivatePostGetwithdrawhistory', code, since, limit, params); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset': currency['id'], }; const response = await this.v3PrivatePostGetdepositaddress (this.extend (request, params)); // { success: true, // message: '', // result: // { Asset: 'ETH', // AssetName: 'Ethereum', // DepositAddress: '0x748c5c8jhksjdfhd507d3aa9', // Currency: 'ETH', // CurrencyName: 'Ethereum' } } const item = response['result']; const address = this.safeString (item, 'DepositAddress'); return { 'currency': code, 'address': this.checkAddress (address), // 'tag': tag, 'info': item, }; } parseLedgerEntryType (type) { // deposits don't seem to appear in here const types = { 'TRADE': 'trade', 'WITHDRAW': 'transaction', }; return this.safeString (types, type, type); } parseLedgerEntry (item, currency = undefined) { // // trade (both sides) // // { // ID: 109660527, // TimeStamp: '2018-11-14 15:12:57.140776', // Asset: 'ETH', // AssetName: 'Ethereum', // Amount: 0.01, // Type: 'TRADE', // Description: 'Trade +, order id 133111123', // Comments: '', // CoinSymbol: 'ETH', // CoinName: 'Ethereum' // } // // { // ID: 109660526, // TimeStamp: '2018-11-14 15:12:57.140776', // Asset: 'BTC', // AssetName: 'Bitcoin', // Amount: -0.00031776, // Type: 'TRADE', // Description: 'Trade -, order id 133111123, fee -0.00000079', // Comments: '', // CoinSymbol: 'BTC', // CoinName: 'Bitcoin' // } // // withdrawal // // { // ID: 104672316, // TimeStamp: '2018-05-03 08:18:19.031831', // Asset: 'DOGE', // AssetName: 'Dogecoin', // Amount: -61893.87864686, // Type: 'WITHDRAW', // Description: 'Withdraw: 61883.87864686 to address DD8tgehNNyYB2iqVazi2W1paaztgcWXtF6; fee 10.00000000', // Comments: '', // CoinSymbol: 'DOGE', // CoinName: 'Dogecoin' // } // const code = this.safeCurrencyCode (this.safeString (item, 'CoinSymbol'), currency); const description = this.safeString (item, 'Description'); const type = this.parseLedgerEntryType (this.safeString (item, 'Type')); let referenceId = undefined; let fee = undefined; const delimiter = (type === 'trade') ? ', ' : '; '; const parts = description.split (delimiter); for (let i = 0; i < parts.length; i++) { let part = parts[i]; if (part.indexOf ('fee') === 0) { part = part.replace ('fee ', ''); let feeCost = parseFloat (part); if (feeCost < 0) { feeCost = -feeCost; } fee = { 'cost': feeCost, 'currency': code, }; } else if (part.indexOf ('order id') === 0) { referenceId = part.replace ('order id ', ''); } // // does not belong to Ledger, related to parseTransaction // // if (part.indexOf ('Withdraw') === 0) { // const details = part.split (' to address '); // if (details.length > 1) { // address = details[1]; // } // } const timestamp = this.parse8601 (this.safeString (item, 'TimeStamp')); let amount = this.safeFloat (item, 'Amount'); let direction = undefined; if (amount !== undefined) { direction = 'in'; if (amount < 0) { direction = 'out'; amount = -amount; } } const id = this.safeString (item, 'ID'); return { 'id': id, 'info': item, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'direction': direction, 'account': undefined, 'referenceId': referenceId, 'referenceAccount': undefined, 'type': type, 'currency': code, 'amount': amount, 'before': undefined, 'after': undefined, 'status': 'ok', 'fee': fee, }; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); // only seems to return 100 items and there is no documented way to change page size or offset const request = { }; const response = await this.v3PrivatePostGetmytransactions (this.extend (request, params)); const items = response['result']; return this.parseLedger (items, code, since, limit); } parseOrder (order, market = undefined) { // // fetchClosedOrders // // { OrderID: 89742658, // Exchange: 'DOGE_BTC', // Type: 'BUY', // Quantity: 10000, // QuantityRemaining: 0, // QuantityBaseTraded: 0, // Price: 6.6e-7, // Status: 'OK', // Created: '2018-02-16 08:55:36', // Comments: '' } // // fetchOpenOrders // // { OrderID: 161105302, // Exchange: 'ETH_BTC', // Type: 'SELL', // Quantity: 0.4, // QuantityRemaining: 0.4, // QuantityBaseTraded: 0, // Price: 0.04, // Status: 'OPEN', // Created: '2020-01-22 09:21:27', // Comments: { String: '', Valid: true } const side = this.safeString (order, 'Type').toLowerCase (); const status = this.parseOrderStatus (this.safeString (order, 'Status')); let symbol = undefined; const marketId = this.safeString (order, 'Exchange'); if (marketId === undefined) { if (market !== undefined) { symbol = market['symbol']; } } else { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = this.parseSymbol (marketId); } } let timestamp = undefined; if ('Created' in order) { timestamp = this.parse8601 (order['Created'] + '+00:00'); } let price = this.safeFloat (order, 'Price'); let cost = undefined; const amount = this.safeFloat (order, 'Quantity'); const remaining = this.safeFloat (order, 'QuantityRemaining'); let filled = undefined; if (amount !== undefined && remaining !== undefined) { filled = amount - remaining; } if (!cost) { if (price && filled) { cost = price * filled; } } if (!price) { if (cost && filled) { price = cost / filled; } } const average = this.safeFloat (order, 'PricePerUnit'); const id = this.safeString (order, 'OrderID'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } parseOrderStatus (status) { const statuses = { 'OK': 'closed', 'OPEN': 'open', 'CANCELED': 'canceled', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // deposit: // // { ID: 118698752, // Timestamp: '2020-01-21 11:16:09', // Asset: 'ETH', // Amount: 1, // TransactionID: '', // Status: 'CONFIRMED', // Label: '0x748c5c8228d0c596f4d07f338blah', // Symbol: 'ETH' } // // withdrawal: // // { ID: 689281, // Timestamp: '2019-07-05 13:14:43', // Asset: 'BTC', // Amount: -0.108959, // TransactionID: 'da48d6901fslfjsdjflsdjfls852b87e362cad1', // Status: 'CONFIRMED', // Label: '0.1089590;35wztHPMgrebFvvblah;0.00100000', // Symbol: 'BTC' } // const id = this.safeString (transaction, 'ID'); let amount = this.safeFloat (transaction, 'Amount'); let type = 'deposit'; if (amount < 0) { amount = Math.abs (amount); type = 'withdrawal'; } const currencyId = this.safeString (transaction, 'Asset'); const code = this.safeCurrencyCode (currencyId, currency); const label = this.safeString (transaction, 'Label'); const timestamp = this.parse8601 (this.safeString (transaction, 'Timestamp')); let txid = this.safeString (transaction, 'TransactionID'); let address = undefined; let feeCost = undefined; const labelParts = label.split (';'); if (labelParts.length === 3) { amount = parseFloat (labelParts[0]); address = labelParts[1]; feeCost = parseFloat (labelParts[2]); } else { address = label; } let fee = undefined; if (feeCost !== undefined) { fee = { 'currency': code, 'cost': feeCost, }; } let status = 'ok'; if (txid === 'CANCELED') { txid = undefined; status = 'canceled'; } return { 'info': transaction, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'id': id, 'currency': code, 'amount': amount, 'address': address, 'tag': undefined, 'status': status, 'type': type, 'updated': undefined, 'txid': txid, 'fee': fee, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname, }) + '/'; if (api === 'v3Private') { this.checkRequiredCredentials (); const request = { 'apikey': this.apiKey, 'nonce': this.nonce (), }; url += path + '?' + this.urlencode (this.extend (request, params)); const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512'); headers = { 'apisign': signature }; } else { url += path + '?' + this.urlencode (params); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // examples... // {"success":false,"message":"Erro: Order is not open.","result":""} <-- 'error' is spelt wrong // {"success":false,"message":"Error: Very low volume.","result":"ERR_LOW_VOLUME"} // {"success":false,"message":"Error: Insuficient Balance","result":"ERR_INSUFICIENT_BALANCE"} // if (body[0] === '{') { const success = this.safeValue (response, 'success'); if (success === undefined) { throw new ExchangeError (this.id + ': malformed response: ' + this.json (response)); } if (!success) { const feedback = this.id + ' ' + body; const errorCode = this.safeString (response, 'result'); this.throwBroadlyMatchedException (this.exceptions['broad'], errorCode, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); const errorMessage = this.safeString (response, 'message'); this.throwBroadlyMatchedException (this.exceptions['broad'], errorMessage, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], errorMessage, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9}],47:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, InvalidOrder, AuthenticationError, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class braziliex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'braziliex', 'name': 'Braziliex', 'countries': [ 'BR' ], 'rateLimit': 1000, 'has': { 'fetchCurrencies': true, 'fetchTickers': true, 'fetchOpenOrders': true, 'fetchMyTrades': true, 'fetchDepositAddress': true, 'fetchOrder': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/34703593-c4498674-f504-11e7-8d14-ff8e44fb78c1.jpg', 'api': 'https://braziliex.com/api/v1', 'www': 'https://braziliex.com/', 'doc': 'https://braziliex.com/exchange/api.php', 'fees': 'https://braziliex.com/exchange/fees.php', 'referral': 'https://braziliex.com/?ref=5FE61AB6F6D67DA885BC98BA27223465', }, 'api': { 'public': { 'get': [ 'currencies', 'ticker', 'ticker/{market}', 'orderbook/{market}', 'tradehistory/{market}', ], }, 'private': { 'post': [ 'balance', 'complete_balance', 'open_orders', 'trade_history', 'deposit_address', 'sell', 'buy', 'cancel_order', 'order_status', ], }, }, 'commonCurrencies': { 'EPC': 'Epacoin', 'ABC': 'Anti Bureaucracy Coin', }, 'fees': { 'trading': { 'maker': 0.005, 'taker': 0.005, }, }, 'precision': { 'amount': 8, 'price': 8, }, 'options': { 'fetchCurrencies': { 'expires': 1000, // 1 second }, }, }); } async fetchCurrenciesFromCache (params = {}) { // this method is now redundant // currencies are now fetched before markets const options = this.safeValue (this.options, 'fetchCurrencies', {}); const timestamp = this.safeInteger (options, 'timestamp'); const expires = this.safeInteger (options, 'expires', 1000); const now = this.milliseconds (); if ((timestamp === undefined) || ((now - timestamp) > expires)) { const response = await this.publicGetCurrencies (params); this.options['fetchCurrencies'] = this.extend (options, { 'response': response, 'timestamp': now, }); } return this.safeValue (this.options['fetchCurrencies'], 'response'); } async fetchCurrencies (params = {}) { const response = await this.fetchCurrenciesFromCache (params); // // { // brl: { // name: "Real", // withdrawal_txFee: 0.0075, // txWithdrawalFee: 9, // MinWithdrawal: 30, // minConf: 1, // minDeposit: 0, // txDepositFee: 0, // txDepositPercentageFee: 0, // minAmountTradeFIAT: 5, // minAmountTradeBTC: 0.0001, // minAmountTradeUSDT: 0.0001, // decimal: 8, // decimal_withdrawal: 8, // active: 1, // dev_active: 1, // under_maintenance: 0, // order: "010", // is_withdrawal_active: 1, // is_deposit_active: 1, // is_token_erc20: 0, // is_fiat: 1, // gateway: 0, // }, // btc: { // name: "Bitcoin", // txWithdrawalMinFee: 0.000125, // txWithdrawalFee: 0.00015625, // MinWithdrawal: 0.0005, // minConf: 1, // minDeposit: 0, // txDepositFee: 0, // txDepositPercentageFee: 0, // minAmountTradeFIAT: 5, // minAmountTradeBTC: 0.0001, // minAmountTradeUSDT: 0.0001, // decimal: 8, // decimal_withdrawal: 8, // active: 1, // dev_active: 1, // under_maintenance: 0, // order: "011", // is_withdrawal_active: 1, // is_deposit_active: 1, // is_token_erc20: 0, // is_fiat: 0, // gateway: 1, // } // } // this.options['currencies'] = { 'timestamp': this.milliseconds (), 'response': response, }; const ids = Object.keys (response); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const currency = response[id]; const precision = this.safeInteger (currency, 'decimal'); const code = this.safeCurrencyCode (id); let active = this.safeInteger (currency, 'active') === 1; const maintenance = this.safeInteger (currency, 'under_maintenance'); if (maintenance !== 0) { active = false; } const canWithdraw = this.safeInteger (currency, 'is_withdrawal_active') === 1; const canDeposit = this.safeInteger (currency, 'is_deposit_active') === 1; if (!canWithdraw || !canDeposit) { active = false; } result[code] = { 'id': id, 'code': code, 'name': currency['name'], 'active': active, 'precision': precision, 'funding': { 'withdraw': { 'active': canWithdraw, 'fee': this.safeFloat (currency, 'txWithdrawalFee'), }, 'deposit': { 'active': canDeposit, 'fee': this.safeFloat (currency, 'txDepositFee'), }, }, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'MinWithdrawal'), 'max': Math.pow (10, precision), }, 'deposit': { 'min': this.safeFloat (currency, 'minDeposit'), 'max': undefined, }, }, 'info': currency, }; } return result; } async fetchMarkets (params = {}) { const currencies = await this.fetchCurrenciesFromCache (params); const response = await this.publicGetTicker (); // // { // btc_brl: { // active: 1, // market: 'btc_brl', // last: 14648, // percentChange: -0.95, // baseVolume24: 27.856, // quoteVolume24: 409328.039, // baseVolume: 27.856, // quoteVolume: 409328.039, // highestBid24: 14790, // lowestAsk24: 14450.01, // highestBid: 14450.37, // lowestAsk: 14699.98 // }, // ... // } // const ids = Object.keys (response); const result = []; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = response[id]; const [ baseId, quoteId ] = id.split ('_'); const uppercaseBaseId = baseId.toUpperCase (); const uppercaseQuoteId = quoteId.toUpperCase (); const base = this.safeCurrencyCode (uppercaseBaseId); const quote = this.safeCurrencyCode (uppercaseQuoteId); const symbol = base + '/' + quote; const baseCurrency = this.safeValue (currencies, baseId, {}); const quoteCurrency = this.safeValue (currencies, quoteId, {}); const quoteIsFiat = this.safeInteger (quoteCurrency, 'is_fiat', 0); let minCost = undefined; if (quoteIsFiat) { minCost = this.safeFloat (baseCurrency, 'minAmountTradeFIAT'); } else { minCost = this.safeFloat (baseCurrency, 'minAmountTrade' + uppercaseQuoteId); } const isActive = this.safeInteger (market, 'active'); const active = (isActive === 1); const precision = { 'amount': 8, 'price': 8, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': minCost, 'max': undefined, }, }, 'info': market, }); } return result; } parseTicker (ticker, market = undefined) { let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.milliseconds (); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'highestBid24'), 'low': this.safeFloat (ticker, 'lowestAsk24'), 'bid': this.safeFloat (ticker, 'highestBid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'lowestAsk'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeFloat (ticker, 'percentChange'), 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'baseVolume24'), 'quoteVolume': this.safeFloat (ticker, 'quoteVolume24'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTickerMarket (this.extend (request, params)); return this.parseTicker (response, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker (params); const result = {}; const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const marketId = ids[i]; const market = this.markets_by_id[marketId]; const symbol = market['symbol']; result[symbol] = this.parseTicker (response[marketId], market); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), }; const response = await this.publicGetOrderbookMarket (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (this.safeString2 (trade, 'date_exec', 'date')); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const cost = this.safeFloat (trade, 'total'); const orderId = this.safeString (trade, 'order_number'); const type = 'limit'; const side = this.safeString (trade, 'type'); const id = this.safeString (trade, '_id'); return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTradehistoryMarket (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const balances = await this.privatePostCompleteBalance (params); const result = { 'info': balances }; const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const balance = balances[currencyId]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['total'] = this.safeFloat (balance, 'total'); result[code] = account; } return this.parseBalance (result); } parseOrder (order, market = undefined) { // // { // "order_number":"58ee441d05f8233fadabfb07", // "type":"buy", // "market":"ltc_btc", // "price":"0.01000000", // "amount":"0.00200000", // "total":"0.00002000", // "progress":"1.0000", // "date":"2017-03-12 15:13:33" // } // let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'market'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } let timestamp = this.safeInteger (order, 'timestamp'); if (timestamp === undefined) { timestamp = this.parse8601 (this.safeString (order, 'date')); } const price = this.safeFloat (order, 'price'); const cost = this.safeFloat (order, 'total', 0.0); const amount = this.safeFloat (order, 'amount'); const filledPercentage = this.safeFloat (order, 'progress'); const filled = amount * filledPercentage; const remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); let info = order; if ('info' in info) { info = order['info']; } const id = this.safeString (order, 'order_number'); const fee = this.safeValue (order, 'fee'); // propagated from createOrder const status = (filledPercentage === 1.0) ? 'closed' : 'open'; return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': 'limit', 'side': order['type'], 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': fee, 'info': info, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const method = 'privatePost' + this.capitalize (side); const request = { 'market': market['id'], // 'price': this.priceToPrecision (symbol, price), // 'amount': this.amountToPrecision (symbol, amount), 'price': price, 'amount': amount, }; const response = await this[method] (this.extend (request, params)); const success = this.safeInteger (response, 'success'); if (success !== 1) { throw new InvalidOrder (this.id + ' ' + this.json (response)); } let parts = response['message'].split (' / '); parts = parts.slice (1); const feeParts = parts[5].split (' '); const order = this.parseOrder ({ 'timestamp': this.milliseconds (), 'order_number': response['order_number'], 'type': parts[0].toLowerCase (), 'market': parts[0].toLowerCase (), 'amount': parts[2].split (' ')[1], 'price': parts[3].split (' ')[1], 'total': parts[4].split (' ')[1], 'fee': { 'cost': parseFloat (feeParts[1]), 'currency': feeParts[2], }, 'progress': '0.0', 'info': response, }, market); const id = order['id']; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_number': id, 'market': market['id'], }; return await this.privatePostCancelOrder (this.extend (request, params)); } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_number': id, 'market': market['id'], }; const response = await this.privatePostOrderStatus (this.extend (request, params)); return this.parseOrder (response, market); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.privatePostOpenOrders (this.extend (request, params)); return this.parseOrders (response['order_open'], market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.privatePostTradeHistory (this.extend (request, params)); return this.parseTrades (response['trade_history'], market, since, limit); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privatePostDepositAddress (this.extend (request, params)); const address = this.safeString (response, 'deposit_address'); this.checkAddress (address); const tag = this.safeString (response, 'payment_id'); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + api; let query = this.omit (params, this.extractParams (path)); if (api === 'public') { url += '/' + this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); query = this.extend ({ 'command': path, 'nonce': this.nonce (), }, query); body = this.urlencode (query); const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); headers = { 'Content-type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': signature, }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ((typeof response === 'string') && (response.length < 1)) { throw new ExchangeError (this.id + ' returned empty response'); } if ('success' in response) { const success = this.safeInteger (response, 'success'); if (success === 0) { const message = this.safeString (response, 'message'); if (message === 'Invalid APIKey') { throw new AuthenticationError (message); } throw new ExchangeError (message); } } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],48:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, DDoSProtection, InvalidOrder, InsufficientFunds } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class btcalpha extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'btcalpha', 'name': 'BTC-Alpha', 'countries': [ 'US' ], 'version': 'v1', 'has': { 'fetchTicker': false, 'fetchOHLCV': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, }, 'timeframes': { '1m': '1', '5m': '5', '15m': '15', '30m': '30', '1h': '60', '4h': '240', '1d': 'D', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/42625213-dabaa5da-85cf-11e8-8f99-aa8f8f7699f0.jpg', 'api': 'https://btc-alpha.com/api', 'www': 'https://btc-alpha.com', 'doc': 'https://btc-alpha.github.io/api-docs', 'fees': 'https://btc-alpha.com/fees/', 'referral': 'https://btc-alpha.com/?r=123788', }, 'api': { 'public': { 'get': [ 'currencies/', 'pairs/', 'orderbook/{pair_name}/', 'exchanges/', 'charts/{pair}/{type}/chart/', ], }, 'private': { 'get': [ 'wallets/', 'orders/own/', 'order/{id}/', 'exchanges/own/', 'deposits/', 'withdraws/', ], 'post': [ 'order/', 'order-cancel/', ], }, }, 'fees': { 'trading': { 'maker': 0.2 / 100, 'taker': 0.2 / 100, }, 'funding': { 'withdraw': { 'BTC': 0.00135, 'LTC': 0.0035, 'XMR': 0.018, 'ZEC': 0.002, 'ETH': 0.01, 'ETC': 0.01, 'SIB': 1.5, 'CCRB': 4, 'PZM': 0.05, 'ITI': 0.05, 'DCY': 5, 'R': 5, 'ATB': 0.05, 'BRIA': 0.05, 'KZC': 0.05, 'HWC': 1, 'SPA': 1, 'SMS': 0.001, 'REC': 0.01, 'SUP': 1, 'BQ': 100, 'GDS': 0.1, 'EVN': 300, 'TRKC': 0.01, 'UNI': 1, 'STN': 1, 'BCH': undefined, 'QBIC': 0.5, }, }, }, 'commonCurrencies': { 'CBC': 'Cashbery', }, 'exceptions': { 'exact': {}, 'broad': { 'Out of balance': InsufficientFunds, // {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"} }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetPairs (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'name'); const baseId = this.safeString (market, 'currency1'); const quoteId = this.safeString (market, 'currency2'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': 8, 'price': this.safeInteger (market, 'price_precision'), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minimum_order_size'), 'max': this.safeFloat (market, 'maximum_order_size'), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, 'baseId': undefined, 'quoteId': undefined, }); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair_name': this.marketId (symbol), }; if (limit) { request['limit_sell'] = limit; request['limit_buy'] = limit; } const response = await this.publicGetOrderbookPairName (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'buy', 'sell', 'price', 'amount'); } parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) { const result = []; for (let i = 0; i < bidasks.length; i++) { const bidask = bidasks[i]; if (bidask) { result.push (this.parseBidAsk (bidask, priceKey, amountKey)); } } return result; } parseTrade (trade, market = undefined) { let symbol = undefined; if (market === undefined) { market = this.safeValue (this.marketsById, trade['pair']); } if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeTimestamp (trade, 'timestamp'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = parseFloat (this.costToPrecision (symbol, price * amount)); } } const id = this.safeString2 (trade, 'id', 'tid'); const side = this.safeString2 (trade, 'my_side', 'side'); const orderId = this.safeString (trade, 'o_id'); return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': 'limit', 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const trades = await this.publicGetExchanges (this.extend (request, params)); return this.parseTrades (trades, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { return [ this.safeTimestamp (ohlcv, 'time'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'type': this.timeframes[timeframe], }; if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['since'] = parseInt (since / 1000); } const response = await this.publicGetChartsPairTypeChart (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetWallets (params); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['used'] = this.safeFloat (balance, 'reserve'); account['total'] = this.safeFloat (balance, 'balance'); result[code] = account; } return this.parseBalance (result); } parseOrderStatus (status) { const statuses = { '1': 'open', '2': 'canceled', '3': 'closed', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { let symbol = undefined; if (market === undefined) { market = this.safeValue (this.marketsById, order['pair']); } if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeTimestamp (order, 'date'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount'); const status = this.parseOrderStatus (this.safeString (order, 'status')); const id = this.safeString2 (order, 'oid', 'id'); let trades = this.safeValue (order, 'trades', []); trades = this.parseTrades (trades, market); const side = this.safeString2 (order, 'my_side', 'type'); let filled = undefined; const numTrades = trades.length; if (numTrades > 0) { filled = 0.0; for (let i = 0; i < numTrades; i++) { filled = this.sum (filled, trades[i]['amount']); } } let remaining = undefined; if ((amount !== undefined) && (amount > 0) && (filled !== undefined)) { remaining = Math.max (0, amount - filled); } return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'status': status, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': undefined, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': trades, 'fee': undefined, 'info': order, 'lastTradeTimestamp': undefined, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'type': side, 'amount': amount, 'price': this.priceToPrecision (symbol, price), }; const response = await this.privatePostOrder (this.extend (request, params)); if (!response['success']) { throw new InvalidOrder (this.id + ' ' + this.json (response)); } const order = this.parseOrder (response, market); amount = (order['amount'] > 0) ? order['amount'] : amount; return this.extend (order, { 'amount': amount, }); } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'order': id, }; const response = await this.privatePostOrderCancel (this.extend (request, params)); return response; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const order = await this.privateGetOrderId (this.extend (request, params)); return this.parseOrder (order); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const orders = await this.privateGetOrdersOwn (this.extend (request, params)); return this.parseOrders (orders, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': '1', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': '3', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbol !== undefined) { const market = this.market (symbol); request['pair'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const trades = await this.privateGetExchangesOwn (this.extend (request, params)); return this.parseTrades (trades, undefined, since, limit); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const query = this.urlencode (this.keysort (this.omit (params, this.extractParams (path)))); let url = this.urls['api'] + '/'; if (path !== 'charts/{pair}/{type}/chart/') { url += 'v1/'; } url += this.implodeParams (path, params); headers = { 'Accept': 'application/json' }; if (api === 'public') { if (query.length) { url += '?' + query; } } else { this.checkRequiredCredentials (); let payload = this.apiKey; if (method === 'POST') { headers['Content-Type'] = 'application/x-www-form-urlencoded'; body = query; payload += body; } else if (query.length) { url += '?' + query; } headers['X-KEY'] = this.apiKey; headers['X-SIGN'] = this.hmac (this.encode (payload), this.encode (this.secret)); headers['X-NONCE'] = this.nonce ().toString (); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // // {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"} // const error = this.safeString (response, 'error'); const feedback = this.id + ' ' + body; if (error !== undefined) { this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); } if (code === 401 || code === 403) { throw new AuthenticationError (feedback); } else if (code === 429) { throw new DDoSProtection (feedback); } if (code < 400) { return; } throw new ExchangeError (feedback); } }; },{"./base/Exchange":7,"./base/errors":9}],49:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, InsufficientFunds, InvalidOrder, AuthenticationError, PermissionDenied, InvalidNonce, OrderNotFound, DDoSProtection } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class btcbox extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'btcbox', 'name': 'BtcBox', 'countries': [ 'JP' ], 'rateLimit': 1000, 'version': 'v1', 'has': { 'CORS': false, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchTickers': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/31275803-4df755a8-aaa1-11e7-9abb-11ec2fad9f2d.jpg', 'api': 'https://www.btcbox.co.jp/api', 'www': 'https://www.btcbox.co.jp/', 'doc': 'https://www.btcbox.co.jp/help/asm', 'fees': 'https://support.btcbox.co.jp/hc/en-us/articles/360001235694-Fees-introduction', }, 'api': { 'public': { 'get': [ 'depth', 'orders', 'ticker', ], }, 'private': { 'post': [ 'balance', 'trade_add', 'trade_cancel', 'trade_list', 'trade_view', 'wallet', ], }, }, 'markets': { 'BTC/JPY': { 'id': 'btc', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy', 'taker': 0.05 / 100, 'maker': 0.05 / 100 }, 'ETH/JPY': { 'id': 'eth', 'symbol': 'ETH/JPY', 'base': 'ETH', 'quote': 'JPY', 'baseId': 'eth', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, 'LTC/JPY': { 'id': 'ltc', 'symbol': 'LTC/JPY', 'base': 'LTC', 'quote': 'JPY', 'baseId': 'ltc', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, 'BCH/JPY': { 'id': 'bch', 'symbol': 'BCH/JPY', 'base': 'BCH', 'quote': 'JPY', 'baseId': 'bch', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, }, 'exceptions': { '104': AuthenticationError, '105': PermissionDenied, '106': InvalidNonce, '107': InvalidOrder, // price should be an integer '200': InsufficientFunds, '201': InvalidOrder, // amount too small '202': InvalidOrder, // price should be [0 : 1000000] '203': OrderNotFound, '401': OrderNotFound, // cancel canceled, closed or non-existent order '402': DDoSProtection, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostBalance (params); const result = { 'info': response }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currency = this.currency (code); const currencyId = currency['id']; const free = currencyId + '_balance'; if (free in response) { const account = this.account (); const used = currencyId + '_lock'; account['free'] = this.safeFloat (response, free); account['used'] = this.safeFloat (response, used); result[code] = account; } } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = {}; const numSymbols = this.symbols.length; if (numSymbols > 1) { request['coin'] = market['baseId']; } const response = await this.publicGetDepth (this.extend (request, params)); return this.parseOrderBook (response); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, 'volume'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = {}; const numSymbols = this.symbols.length; if (numSymbols > 1) { request['coin'] = market['baseId']; } const response = await this.publicGetTicker (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (trade, 'tid'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } const type = undefined; const side = this.safeString (trade, 'type'); return { 'info': trade, 'id': id, 'order': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = {}; const numSymbols = this.symbols.length; if (numSymbols > 1) { request['coin'] = market['baseId']; } const response = await this.publicGetOrders (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'amount': amount, 'price': price, 'type': side, 'coin': market['baseId'], }; const response = await this.privatePostTradeAdd (this.extend (request, params)); // // { // "result":true, // "id":"11" // } // return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); // a special case for btcbox – default symbol is BTC/JPY if (symbol === undefined) { symbol = 'BTC/JPY'; } const market = this.market (symbol); const request = { 'id': id, 'coin': market['baseId'], }; const response = await this.privatePostTradeCancel (this.extend (request, params)); // // {"result":true, "id":"11"} // return this.parseOrder (response, market); } parseOrderStatus (status) { const statuses = { // TODO: complete list 'part': 'open', // partially or not at all executed 'all': 'closed', // fully executed 'cancelled': 'canceled', 'closed': 'closed', // never encountered, seems to be bug in the doc 'no': 'closed', // not clarified in the docs... }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "id":11, // "datetime":"2014-10-21 10:47:20", // "type":"sell", // "price":42000, // "amount_original":1.2, // "amount_outstanding":1.2, // "status":"closed", // "trades":[] // } // const id = this.safeString (order, 'id'); const datetimeString = this.safeString (order, 'datetime'); let timestamp = undefined; if (datetimeString !== undefined) { timestamp = this.parse8601 (order['datetime'] + '+09:00'); // Tokyo time } const amount = this.safeFloat (order, 'amount_original'); const remaining = this.safeFloat (order, 'amount_outstanding'); let filled = undefined; if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; } } const price = this.safeFloat (order, 'price'); let cost = undefined; if (price !== undefined) { if (filled !== undefined) { cost = filled * price; } } // status is set by fetchOrder method only let status = this.parseOrderStatus (this.safeString (order, 'status')); // fetchOrders do not return status, use heuristic if (status === undefined) { if (remaining !== undefined && remaining === 0) { status = 'closed'; } } const trades = undefined; // todo: this.parseTrades (order['trades']); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const side = this.safeString (order, 'type'); return { 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'amount': amount, 'remaining': remaining, 'filled': filled, 'side': side, 'type': undefined, 'status': status, 'symbol': symbol, 'price': price, 'cost': cost, 'trades': trades, 'fee': undefined, 'info': order, 'average': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); // a special case for btcbox – default symbol is BTC/JPY if (symbol === undefined) { symbol = 'BTC/JPY'; } const market = this.market (symbol); const request = this.extend ({ 'id': id, 'coin': market['baseId'], }, params); const response = await this.privatePostTradeView (this.extend (request, params)); return this.parseOrder (response, market); } async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); // a special case for btcbox – default symbol is BTC/JPY if (symbol === undefined) { symbol = 'BTC/JPY'; } const market = this.market (symbol); const request = { 'type': type, // 'open' or 'all' 'coin': market['baseId'], }; const response = await this.privatePostTradeList (this.extend (request, params)); const orders = this.parseOrders (response, market, since, limit); // status (open/closed/canceled) is undefined // btcbox does not return status, but we know it's 'open' as we queried for open orders if (type === 'open') { for (let i = 0; i < orders.length; i++) { orders[i]['status'] = 'open'; } } return orders; } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByType ('all', symbol, since, limit, params); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByType ('open', symbol, since, limit, params); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const query = this.extend ({ 'key': this.apiKey, 'nonce': nonce, }, params); const request = this.urlencode (query); const secret = this.hash (this.encode (this.secret)); query['signature'] = this.hmac (this.encode (request), this.encode (secret)); body = this.urlencode (query); headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // resort to defaultErrorHandler } // typical error response: {"result":false,"code":"401"} if (httpCode >= 400) { return; // resort to defaultErrorHandler } const result = this.safeValue (response, 'result'); if (result === undefined || result === true) { return; // either public API (no error codes expected) or success } const code = this.safeValue (response, 'code'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, code, feedback); throw new ExchangeError (feedback); // unknown message } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let response = await this.fetch2 (path, api, method, params, headers, body); if (typeof response === 'string') { // sometimes the exchange returns whitespace prepended to json response = this.strip (response); if (!this.isJsonEncodedObject (response)) { throw new ExchangeError (this.id + ' ' + response); } response = JSON.parse (response); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],50:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, OrderNotFound, ArgumentsRequired, InvalidOrder, DDoSProtection } = require ('./base/errors'); const { ROUND } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class btcmarkets extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'btcmarkets', 'name': 'BTC Markets', 'countries': [ 'AU' ], // Australia 'rateLimit': 1000, // market data cached for 1 second (trades cached for 2 seconds) 'has': { 'CORS': false, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchClosedOrders': 'emulated', 'fetchOpenOrders': true, 'fetchMyTrades': true, 'cancelOrders': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/29142911-0e1acfc2-7d5c-11e7-98c4-07d9532b29d7.jpg', 'api': { 'public': 'https://api.btcmarkets.net', 'private': 'https://api.btcmarkets.net', 'web': 'https://btcmarkets.net/data', }, 'www': 'https://btcmarkets.net', 'doc': 'https://github.com/BTCMarkets/API', }, 'api': { 'public': { 'get': [ 'market/{id}/tick', 'market/{id}/orderbook', 'market/{id}/trades', 'v2/market/{id}/tickByTime/{timeframe}', 'v2/market/{id}/trades', 'v2/market/active', ], }, 'private': { 'get': [ 'account/balance', 'account/{id}/tradingfee', 'fundtransfer/history', 'v2/order/open', 'v2/order/open/{id}', 'v2/order/history/{instrument}/{currency}/', 'v2/order/trade/history/{id}', 'v2/transaction/history/{currency}', ], 'post': [ 'fundtransfer/withdrawCrypto', 'fundtransfer/withdrawEFT', 'order/create', 'order/cancel', 'order/history', 'order/open', 'order/trade/history', 'order/createBatch', // they promise it's coming soon... 'order/detail', ], }, 'web': { 'get': [ 'market/BTCMarkets/{id}/tickByTime', ], }, }, 'timeframes': { '1m': 'minute', '1h': 'hour', '1d': 'day', }, 'exceptions': { '3': InvalidOrder, '6': DDoSProtection, }, 'fees': { 'percentage': true, 'tierBased': true, 'maker': -0.05 / 100, 'taker': 0.20 / 100, }, 'options': { 'fees': { 'AUD': { 'maker': 0.85 / 100, 'taker': 0.85 / 100, }, }, }, }); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['since'] = since; } const response = await this.privateGetFundtransferHistory (this.extend (request, params)); const transactions = response['fundTransfers']; return this.parseTransactions (transactions, undefined, since, limit); } parseTransactionStatus (status) { // todo: find more statuses const statuses = { 'Complete': 'ok', }; return this.safeString (statuses, status, status); } parseTransaction (item, currency = undefined) { // // { // status: 'Complete', // fundTransferId: 1904311906, // description: 'ETH withdraw from [me@email.com] to Address: 0xF123aa44FadEa913a7da99cc2eE202Db684Ce0e3 amount: 8.28965701 fee: 0.00000000', // creationTime: 1529418358525, // currency: 'ETH', // amount: 828965701, // fee: 0, // transferType: 'WITHDRAW', // errorMessage: null, // lastUpdate: 1529418376754, // cryptoPaymentDetail: { // address: '0xF123aa44FadEa913a7da99cc2eE202Db684Ce0e3', // txId: '0x8fe483b6f9523559b9ebffb29624f98e86227d2660d4a1fd4785d45e51c662c2' // } // } // // { // status: 'Complete', // fundTransferId: 494077500, // description: 'BITCOIN Deposit, B 0.1000', // creationTime: 1501077601015, // currency: 'BTC', // amount: 10000000, // fee: 0, // transferType: 'DEPOSIT', // errorMessage: null, // lastUpdate: 1501077601133, // cryptoPaymentDetail: null // } // // { // "fee": 0, // "amount": 56, // "status": "Complete", // "currency": "BCHABC", // "lastUpdate": 1542339164044, // "description": "BitcoinCashABC Deposit, P 0.00000056", // "creationTime": 1542339164003, // "errorMessage": null, // "transferType": "DEPOSIT", // "fundTransferId": 2527326972, // "cryptoPaymentDetail": null // } // const timestamp = this.safeInteger (item, 'creationTime'); const lastUpdate = this.safeInteger (item, 'lastUpdate'); const transferType = this.safeString (item, 'transferType'); const cryptoPaymentDetail = this.safeValue (item, 'cryptoPaymentDetail', {}); const address = this.safeString (cryptoPaymentDetail, 'address'); const txid = this.safeString (cryptoPaymentDetail, 'txId'); let type = undefined; if (transferType === 'DEPOSIT') { type = 'deposit'; } else if (transferType === 'WITHDRAW') { type = 'withdrawal'; } else { type = transferType; } const fee = this.safeFloat (item, 'fee'); const status = this.parseTransactionStatus (this.safeString (item, 'status')); const ccy = this.safeString (item, 'currency'); const code = this.safeCurrencyCode (ccy); // todo: this logic is duplicated below let amount = this.safeFloat (item, 'amount'); if (amount !== undefined) { amount = amount * 1e-8; } return { 'id': this.safeString (item, 'fundTransferId'), 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': undefined, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': lastUpdate, 'fee': { 'currency': code, 'cost': fee, }, 'info': item, }; } async fetchMarkets (params = {}) { const response = await this.publicGetV2MarketActive (params); const result = []; const markets = this.safeValue (response, 'markets'); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const baseId = this.safeString (market, 'instrument'); const quoteId = this.safeString (market, 'currency'); const id = baseId + '/' + quoteId; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const fees = this.safeValue (this.safeValue (this.options, 'fees', {}), quote, this.fees); let pricePrecision = 2; let amountPrecision = 4; const minAmount = 0.001; // where does it come from? let minPrice = undefined; if (quote === 'AUD') { if ((base === 'XRP') || (base === 'OMG')) { pricePrecision = 4; } amountPrecision = -Math.log10 (minAmount); minPrice = Math.pow (10, -pricePrecision); } const precision = { 'amount': amountPrecision, 'price': pricePrecision, }; const limits = { 'amount': { 'min': minAmount, 'max': undefined, }, 'price': { 'min': minPrice, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }; result.push ({ 'info': market, 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': undefined, 'maker': fees['maker'], 'taker': fees['taker'], 'limits': limits, 'precision': precision, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const balances = await this.privateGetAccountBalance (params); const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const multiplier = 100000000; let total = this.safeFloat (balance, 'balance'); if (total !== undefined) { total /= multiplier; } let used = this.safeFloat (balance, 'pendingFunds'); if (used !== undefined) { used /= multiplier; } const account = this.account (); account['used'] = used; account['total'] = total; result[code] = account; } return this.parseBalance (result); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // { // "timestamp":1572307200000, // "open":1962218, // "high":1974850, // "low":1962208, // "close":1974850, // "volume":305211315, // } // const multiplier = 100000000; // for price and volume const keys = [ 'open', 'high', 'low', 'close', 'volume' ]; const result = [ this.safeInteger (ohlcv, 'timestamp'), ]; for (let i = 0; i < keys.length; i++) { const key = keys[i]; let value = this.safeFloat (ohlcv, key); if (value !== undefined) { value = value / multiplier; } result.push (value); } return result; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.load_markets (); const market = this.market (symbol); const request = { 'id': market['id'], 'timeframe': this.timeframes[timeframe], // set to true to see candles more recent than the timestamp in the // since parameter, if a since parameter is used, default is false 'indexForward': true, // set to true to see the earliest candles first in the list of // returned candles in chronological order, default is false 'sortForward': true, }; if (since !== undefined) { request['since'] = since; } if (limit !== undefined) { request['limit'] = limit; // default is 3000 } const response = await this.publicGetV2MarketIdTickByTimeTimeframe (this.extend (request, params)); // // { // "success":true, // "paging":{ // "newer":"/v2/market/ETH/BTC/tickByTime/day?indexForward=true&since=1572307200000", // "older":"/v2/market/ETH/BTC/tickByTime/day?since=1457827200000" // }, // "ticks":[ // {"timestamp":1572307200000,"open":1962218,"high":1974850,"low":1962208,"close":1974850,"volume":305211315}, // {"timestamp":1572220800000,"open":1924700,"high":1951276,"low":1909328,"close":1951276,"volume":1086067595}, // {"timestamp":1572134400000,"open":1962155,"high":1962734,"low":1900905,"close":1930243,"volume":790141098}, // ], // } // const ticks = this.safeValue (response, 'ticks', []); return this.parseOHLCVs (ticks, market, timeframe, since, limit); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; const response = await this.publicGetMarketIdOrderbook (this.extend (request, params)); const timestamp = this.safeTimestamp (response, 'timestamp'); return this.parseOrderBook (response, timestamp); } parseTicker (ticker, market = undefined) { const timestamp = this.safeTimestamp (ticker, 'timestamp'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'lastPrice'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'bestBid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'bestAsk'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume24h'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; const response = await this.publicGetMarketIdTick (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (trade, 'tid'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } return { 'info': trade, 'id': id, 'order': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { // 'since': 59868345231, 'id': market['id'], }; const response = await this.publicGetMarketIdTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const multiplier = 100000000; // for price and volume const orderSide = (side === 'buy') ? 'Bid' : 'Ask'; const request = this.ordered ({ 'currency': market['quote'], }); request['currency'] = market['quote']; request['instrument'] = market['base']; request['price'] = parseInt (this.decimalToPrecision (price * multiplier, ROUND, 0)); request['volume'] = parseInt (this.decimalToPrecision (amount * multiplier, ROUND, 0)); request['orderSide'] = orderSide; request['ordertype'] = this.capitalize (type); request['clientRequestId'] = this.nonce ().toString (); const response = await this.privatePostOrderCreate (this.extend (request, params)); const id = this.safeString (response, 'id'); return { 'info': response, 'id': id, }; } async cancelOrders (ids, symbol = undefined, params = {}) { await this.loadMarkets (); for (let i = 0; i < ids.length; i++) { ids[i] = parseInt (ids[i]); } const request = { 'orderIds': ids, }; return await this.privatePostOrderCancel (this.extend (request, params)); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); return await this.cancelOrders ([ id ]); } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; const rate = market[takerOrMaker]; let currency = undefined; let cost = undefined; if (market['quote'] === 'AUD') { currency = market['quote']; cost = parseFloat (this.costToPrecision (symbol, amount * price)); } else { currency = market['base']; cost = parseFloat (this.amountToPrecision (symbol, amount)); } return { 'type': takerOrMaker, 'currency': currency, 'rate': rate, 'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)), }; } parseMyTrade (trade, market) { const multiplier = 100000000; const timestamp = this.safeInteger (trade, 'creationTime'); let side = this.safeFloat (trade, 'side'); side = (side === 'Bid') ? 'buy' : 'sell'; // BTCMarkets always charge in AUD for AUD-related transactions. let feeCurrencyCode = undefined; let symbol = undefined; if (market !== undefined) { feeCurrencyCode = (market['quote'] === 'AUD') ? market['quote'] : market['base']; symbol = market['symbol']; } const id = this.safeString (trade, 'id'); let price = this.safeFloat (trade, 'price'); if (price !== undefined) { price /= multiplier; } let amount = this.safeFloat (trade, 'volume'); if (amount !== undefined) { amount /= multiplier; } let feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { feeCost /= multiplier; } let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } const orderId = this.safeString (trade, 'orderId'); return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'order': orderId, 'symbol': symbol, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': { 'currency': feeCurrencyCode, 'cost': feeCost, }, 'takerOrMaker': undefined, }; } parseMyTrades (trades, market = undefined, since = undefined, limit = undefined) { const result = []; for (let i = 0; i < trades.length; i++) { const trade = this.parseMyTrade (trades[i], market); result.push (trade); } return result; } parseOrder (order, market = undefined) { const multiplier = 100000000; const side = (order['orderSide'] === 'Bid') ? 'buy' : 'sell'; const type = (order['ordertype'] === 'Limit') ? 'limit' : 'market'; const timestamp = this.safeInteger (order, 'creationTime'); if (market === undefined) { market = this.market (order['instrument'] + '/' + order['currency']); } let status = 'open'; if (order['status'] === 'Failed' || order['status'] === 'Cancelled' || order['status'] === 'Partially Cancelled' || order['status'] === 'Error') { status = 'canceled'; } else if (order['status'] === 'Fully Matched' || order['status'] === 'Partially Matched') { status = 'closed'; } const price = this.safeFloat (order, 'price') / multiplier; const amount = this.safeFloat (order, 'volume') / multiplier; const remaining = this.safeFloat (order, 'openVolume', 0.0) / multiplier; const filled = amount - remaining; const trades = this.parseMyTrades (order['trades'], market); const numTrades = trades.length; let cost = filled * price; let average = undefined; let lastTradeTimestamp = undefined; if (numTrades > 0) { cost = 0; for (let i = 0; i < numTrades; i++) { const trade = trades[i]; cost = this.sum (cost, trade['cost']); } if (filled > 0) { average = cost / filled; } lastTradeTimestamp = trades[numTrades - 1]['timestamp']; } const id = this.safeString (order, 'id'); const clientOrderId = this.safeString (order, 'clientRequestId'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': market['symbol'], 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'average': average, 'status': status, 'trades': trades, 'fee': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const ids = [ parseInt (id) ]; const request = { 'orderIds': ids, }; const response = await this.privatePostOrderDetail (this.extend (request, params)); const numOrders = response['orders'].length; if (numOrders < 1) { throw new OrderNotFound (this.id + ' No matching order found: ' + id); } const order = response['orders'][0]; return this.parseOrder (order); } createPaginatedRequest (market, since = undefined, limit = undefined) { limit = (limit === undefined) ? 100 : limit; since = (since === undefined) ? 0 : since; const request = this.ordered ({ 'currency': market['quoteId'], 'instrument': market['baseId'], 'limit': limit, 'since': since, }); return request; } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ': fetchOrders requires a `symbol` argument.'); } await this.loadMarkets (); const market = this.market (symbol); const request = this.createPaginatedRequest (market, since, limit); const response = await this.privatePostOrderHistory (this.extend (request, params)); return this.parseOrders (response['orders'], market); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ': fetchOpenOrders requires a `symbol` argument.'); } await this.loadMarkets (); const market = this.market (symbol); const request = this.createPaginatedRequest (market, since, limit); const response = await this.privatePostOrderOpen (this.extend (request, params)); return this.parseOrders (response['orders'], market); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'closed'); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ': fetchMyTrades requires a `symbol` argument.'); } await this.loadMarkets (); const market = this.market (symbol); const request = this.createPaginatedRequest (market, since, limit); const response = await this.privatePostOrderTradeHistory (this.extend (request, params)); return this.parseMyTrades (response['trades'], market); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const uri = '/' + this.implodeParams (path, params); let url = this.urls['api'][api] + uri; if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); let auth = undefined; headers = { 'apikey': this.apiKey, 'timestamp': nonce, }; if (method === 'POST') { headers['Content-Type'] = 'application/json'; auth = uri + "\n" + nonce + "\n"; // eslint-disable-line quotes body = this.json (params); auth += body; } else { const query = this.keysort (this.omit (params, this.extractParams (path))); let queryString = ''; if (Object.keys (query).length) { queryString = this.urlencode (query); url += '?' + queryString; queryString += "\n"; // eslint-disable-line quotes } auth = uri + "\n" + queryString + nonce + "\n"; // eslint-disable-line quotes } const secret = this.base64ToBinary (this.secret); const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); headers['signature'] = this.decode (signature); } else { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('success' in response) { if (!response['success']) { const error = this.safeString (response, 'errorCode'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, error, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],51:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const coinegg = require ('./coinegg.js'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class btctradeim extends coinegg { describe () { const result = this.deepExtend (super.describe (), { 'id': 'btctradeim', 'name': 'BtcTrade.im', 'countries': [ 'HK' ], 'urls': { 'referral': 'https://m.baobi.com/invite?inv=1765b2', 'logo': 'https://user-images.githubusercontent.com/1294454/36770531-c2142444-1c5b-11e8-91e2-a4d90dc85fe8.jpg', 'api': { 'web': 'https://api.btctrade.im/coin', 'rest': 'https://api.btctrade.im/api/v1', }, 'www': 'https://www.btctrade.im', 'doc': 'https://www.btctrade.im/help.api.html', 'fees': 'https://www.btctrade.im/spend.price.html', }, 'status': { 'status': 'error', 'updated': undefined, 'eta': undefined, 'url': undefined, }, 'fees': { 'trading': { 'maker': 0.2 / 100, 'taker': 0.2 / 100, }, 'funding': { 'withdraw': { 'BTC': 0.001, }, }, }, // see the fix below // 'options': { // 'quoteIds': [ 'btc', 'eth', 'usc' ], // }, }); // a fix for PHP array_merge not overwriting "lists" (integer-indexed arrays) // https://github.com/ccxt/ccxt/issues/3343 result['options']['quoteIds'] = [ 'btc', 'eth', 'usc' ]; return result; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if (api === 'web') { return response; } const data = this.safeValue (response, 'data'); if (data) { const code = this.safeString (response, 'code'); if (code !== '0') { const message = this.safeString (response, 'msg', 'Error'); throw new ExchangeError (message); } return data; } return response; } }; },{"./base/errors":9,"./coinegg.js":64}],52:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class btctradeua extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'btctradeua', 'name': 'BTC Trade UA', 'countries': [ 'UA' ], // Ukraine, 'rateLimit': 3000, 'has': { 'CORS': false, 'createMarketOrder': false, 'fetchOpenOrders': true, 'signIn': true, }, 'urls': { 'referral': 'https://btc-trade.com.ua/registration/22689', 'logo': 'https://user-images.githubusercontent.com/1294454/27941483-79fc7350-62d9-11e7-9f61-ac47f28fcd96.jpg', 'api': 'https://btc-trade.com.ua/api', 'www': 'https://btc-trade.com.ua', 'doc': 'https://docs.google.com/document/d/1ocYA0yMy_RXd561sfG3qEPZ80kyll36HUxvCRe5GbhE/edit', }, 'api': { 'public': { 'get': [ 'deals/{symbol}', 'trades/sell/{symbol}', 'trades/buy/{symbol}', 'japan_stat/high/{symbol}', ], }, 'private': { 'post': [ 'auth', 'ask/{symbol}', 'balance', 'bid/{symbol}', 'buy/{symbol}', 'my_orders/{symbol}', 'order/status/{id}', 'remove/order/{id}', 'sell/{symbol}', ], }, }, 'markets': { 'BCH/UAH': { 'id': 'bch_uah', 'symbol': 'BCH/UAH', 'base': 'BCH', 'quote': 'UAH', 'baseId': 'bch', 'quoteId': 'uah' }, 'BTC/UAH': { 'id': 'btc_uah', 'symbol': 'BTC/UAH', 'base': 'BTC', 'quote': 'UAH', 'baseId': 'btc', 'quoteId': 'uah', 'precision': { 'price': 1 }, 'limits': { 'amount': { 'min': 0.0000000001 }}}, 'DASH/BTC': { 'id': 'dash_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'dash', 'quoteId': 'btc' }, 'DASH/UAH': { 'id': 'dash_uah', 'symbol': 'DASH/UAH', 'base': 'DASH', 'quote': 'UAH', 'baseId': 'dash', 'quoteId': 'uah' }, 'DOGE/BTC': { 'id': 'doge_btc', 'symbol': 'DOGE/BTC', 'base': 'DOGE', 'quote': 'BTC', 'baseId': 'doge', 'quoteId': 'btc' }, 'DOGE/UAH': { 'id': 'doge_uah', 'symbol': 'DOGE/UAH', 'base': 'DOGE', 'quote': 'UAH', 'baseId': 'doge', 'quoteId': 'uah' }, 'ETH/UAH': { 'id': 'eth_uah', 'symbol': 'ETH/UAH', 'base': 'ETH', 'quote': 'UAH', 'baseId': 'eth', 'quoteId': 'uah' }, 'ITI/UAH': { 'id': 'iti_uah', 'symbol': 'ITI/UAH', 'base': 'ITI', 'quote': 'UAH', 'baseId': 'iti', 'quoteId': 'uah' }, 'KRB/UAH': { 'id': 'krb_uah', 'symbol': 'KRB/UAH', 'base': 'KRB', 'quote': 'UAH', 'baseId': 'krb', 'quoteId': 'uah' }, 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, 'LTC/UAH': { 'id': 'ltc_uah', 'symbol': 'LTC/UAH', 'base': 'LTC', 'quote': 'UAH', 'baseId': 'ltc', 'quoteId': 'uah' }, 'NVC/BTC': { 'id': 'nvc_btc', 'symbol': 'NVC/BTC', 'base': 'NVC', 'quote': 'BTC', 'baseId': 'nvc', 'quoteId': 'btc' }, 'NVC/UAH': { 'id': 'nvc_uah', 'symbol': 'NVC/UAH', 'base': 'NVC', 'quote': 'UAH', 'baseId': 'nvc', 'quoteId': 'uah' }, 'PPC/BTC': { 'id': 'ppc_btc', 'symbol': 'PPC/BTC', 'base': 'PPC', 'quote': 'BTC', 'baseId': 'ppc', 'quoteId': 'btc' }, 'SIB/UAH': { 'id': 'sib_uah', 'symbol': 'SIB/UAH', 'base': 'SIB', 'quote': 'UAH', 'baseId': 'sib', 'quoteId': 'uah' }, 'XMR/UAH': { 'id': 'xmr_uah', 'symbol': 'XMR/UAH', 'base': 'XMR', 'quote': 'UAH', 'baseId': 'xmr', 'quoteId': 'uah' }, 'ZEC/UAH': { 'id': 'zec_uah', 'symbol': 'ZEC/UAH', 'base': 'ZEC', 'quote': 'UAH', 'baseId': 'zec', 'quoteId': 'uah' }, }, 'fees': { 'trading': { 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, 'funding': { 'withdraw': { 'BTC': 0.0006, 'LTC': 0.01, 'NVC': 0.01, 'DOGE': 10, }, }, }, }); } async signIn (params = {}) { return await this.privatePostAuth (params); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostBalance (params); const result = { 'info': response }; const balances = this.safeValue (response, 'accounts'); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balance, 'balance'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const bids = await this.publicGetTradesBuySymbol (this.extend (request, params)); const asks = await this.publicGetTradesSellSymbol (this.extend (request, params)); const orderbook = { 'bids': [], 'asks': [], }; if (bids) { if ('list' in bids) { orderbook['bids'] = bids['list']; } } if (asks) { if ('list' in asks) { orderbook['asks'] = asks['list']; } } return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'currency_trade'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; const response = await this.publicGetJapanStatHighSymbol (this.extend (request, params)); const ticker = this.safeValue (response, 'trades'); const timestamp = this.milliseconds (); const result = { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': undefined, 'last': undefined, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': undefined, 'info': ticker, }; const tickerLength = ticker.length; if (tickerLength > 0) { const start = Math.max (tickerLength - 48, 0); for (let i = start; i < ticker.length; i++) { const candle = ticker[i]; if (result['open'] === undefined) { result['open'] = candle[1]; } if ((result['high'] === undefined) || (result['high'] < candle[2])) { result['high'] = candle[2]; } if ((result['low'] === undefined) || (result['low'] > candle[3])) { result['low'] = candle[3]; } if (result['baseVolume'] === undefined) { result['baseVolume'] = -candle[5]; } else { result['baseVolume'] -= candle[5]; } } const last = tickerLength - 1; result['last'] = ticker[last][4]; result['close'] = result['last']; result['baseVolume'] = -1 * result['baseVolume']; } return result; } convertCyrillicMonthNameToString (cyrillic) { const months = { 'января': '01', 'февраля': '02', 'марта': '03', 'апреля': '04', 'мая': '05', 'июня': '06', 'июля': '07', 'августа': '08', 'сентября': '09', 'октября': '10', 'ноября': '11', 'декабря': '12', }; return this.safeString (months, cyrillic); } parseCyrillicDatetime (cyrillic) { const parts = cyrillic.split (' '); let day = parts[0]; const month = this.convertCyrillicMonthNameToString (parts[1]); if (!month) { throw new ExchangeError (this.id + ' parseTrade() undefined month name: ' + cyrillic); } const year = parts[2]; let hms = parts[4]; const hmsLength = hms.length; if (hmsLength === 7) { hms = '0' + hms; } if (day.length === 1) { day = '0' + day; } const ymd = [ year, month, day ].join ('-'); const ymdhms = ymd + 'T' + hms; const timestamp = this.parse8601 (ymdhms); // server reports local time, adjust to UTC let md = [ month, day ].join (''); md = parseInt (md); // a special case for DST // subtract 2 hours during winter if (md < 325 || md > 1028) { return timestamp - 7200000; } // subtract 3 hours during summer return timestamp - 10800000; } parseTrade (trade, market = undefined) { const timestamp = this.parseCyrillicDatetime (this.safeString (trade, 'pub_date')); const id = this.safeString (trade, 'id'); const type = 'limit'; const side = this.safeString (trade, 'type'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amnt_trade'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetDealsSymbol (this.extend (request, params)); // they report each trade twice (once for both of the two sides of the fill) // deduplicate trades for that reason const trades = []; for (let i = 0; i < response.length; i++) { if (response[i]['id'] % 2) { trades.push (response[i]); } } return this.parseTrades (trades, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); const method = 'privatePost' + this.capitalize (side) + 'Id'; const request = { 'count': amount, 'currency1': market['quoteId'], 'currency': market['baseId'], 'price': price, }; return this[method] (this.extend (request, params)); } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'id': id, }; return await this.privatePostRemoveOrderId (this.extend (request, params)); } parseOrder (order, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': this.safeString (order, 'id'), 'clientOrderId': undefined, 'timestamp': timestamp, // until they fix their timestamp 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': 'open', 'symbol': symbol, 'type': undefined, 'side': this.safeString (order, 'type'), 'price': this.safeFloat (order, 'price'), 'amount': this.safeFloat (order, 'amnt_trade'), 'filled': 0, 'remaining': this.safeFloat (order, 'amnt_trade'), 'trades': undefined, 'info': order, 'cost': undefined, 'average': undefined, 'fee': undefined, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.privatePostMyOrdersSymbol (this.extend (request, params)); const orders = this.safeValue (response, 'your_open_orders'); return this.parseOrders (orders, market, since, limit); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += this.implodeParams (path, query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'out_order_id': nonce, 'nonce': nonce, }, query)); const auth = body + this.secret; headers = { 'public-key': this.apiKey, 'api-sign': this.hash (this.encode (auth), 'sha256'), 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],53:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class btcturk extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'btcturk', 'name': 'BTCTurk', 'countries': [ 'TR' ], // Turkey 'rateLimit': 1000, 'has': { 'CORS': true, 'fetchTickers': true, 'fetchOHLCV': true, }, 'timeframes': { '1d': '1d', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27992709-18e15646-64a3-11e7-9fa2-b0950ec7712f.jpg', 'api': 'https://www.btcturk.com/api', 'www': 'https://www.btcturk.com', 'doc': 'https://github.com/BTCTrader/broker-api-docs', }, 'api': { 'public': { 'get': [ 'ohlcdata', // ?last=COUNT 'orderbook', 'ticker', 'trades', // ?last=COUNT (max 50) ], }, 'private': { 'get': [ 'balance', 'openOrders', 'userTransactions', // ?offset=0&limit=25&sort=asc ], 'post': [ 'exchange', 'cancelOrder', ], }, }, 'fees': { 'trading': { 'maker': 0.002 * 1.18, 'taker': 0.003 * 1.18, }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetTicker (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'pair'); let baseId = id.slice (0, 3); let quoteId = id.slice (3, 6); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); baseId = baseId.toLowerCase (); quoteId = quoteId.toLowerCase (); const symbol = base + '/' + quote; const precision = { 'amount': 8, 'price': 8, }; const active = true; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'info': market, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetBalance (params); const result = { 'info': response }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currency = this.currencies[code]; const free = currency['id'] + '_available'; const total = currency['id'] + '_balance'; const used = currency['id'] + '_reserved'; if (free in response) { const account = this.account (); account['free'] = this.safeFloat (response, free); account['total'] = this.safeFloat (response, total); account['used'] = this.safeFloat (response, used); result[code] = account; } } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pairSymbol': market['id'], }; const response = await this.publicGetOrderbook (this.extend (request, params)); const timestamp = this.safeTimestamp (response, 'timestamp'); return this.parseOrderBook (response, timestamp); } parseTicker (ticker, market = undefined) { let symbol = undefined; if (market) { symbol = market['symbol']; } const timestamp = this.safeTimestamp (ticker, 'timestamp'); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker, 'average'), 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const tickers = await this.publicGetTicker (params); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = tickers[i]; const marketId = this.safeString (ticker, 'pair'); let symbol = marketId; let market = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[symbol]; symbol = market['symbol']; } result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const tickers = await this.fetchTickers (params); return this.safeValue2 (tickers, market['id'], symbol); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const id = this.safeString (trade, 'tid'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); // let maxCount = 50; const request = { 'pairSymbol': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { const timestamp = this.parse8601 (this.safeString (ohlcv, 'Time')); return [ timestamp, this.safeFloat (ohlcv, 'Open'), this.safeFloat (ohlcv, 'High'), this.safeFloat (ohlcv, 'Low'), this.safeFloat (ohlcv, 'Close'), this.safeFloat (ohlcv, 'Volume'), ]; } async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = {}; if (limit !== undefined) { request['last'] = limit; } const response = await this.publicGetOhlcdata (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'PairSymbol': this.marketId (symbol), 'OrderType': (side === 'buy') ? 0 : 1, 'OrderMethod': (type === 'market') ? 1 : 0, }; if (type === 'market') { if (!('Total' in params)) { throw new ExchangeError (this.id + ' createOrder requires the "Total" extra parameter for market orders (amount and price are both ignored)'); } } else { request['Price'] = price; request['Amount'] = amount; } const response = await this.privatePostExchange (this.extend (request, params)); const id = this.safeString (response, 'id'); return { 'info': response, 'id': id, }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'id': id, }; return await this.privatePostCancelOrder (this.extend (request, params)); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { if (this.id === 'btctrader') { throw new ExchangeError (this.id + ' is an abstract base API for BTCExchange, BTCTurk'); } let url = this.urls['api'] + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); body = this.urlencode (params); const secret = this.base64ToBinary (this.secret); const auth = this.apiKey + nonce; headers = { 'X-PCK': this.apiKey, 'X-Stamp': nonce, 'X-Signature': this.stringToBase64 (this.hmac (this.encode (auth), secret, 'sha256', 'binary')), 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],54:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { AddressPending, AuthenticationError, ExchangeError, NotSupported, PermissionDenied, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class buda extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'buda', 'name': 'Buda', 'countries': [ 'AR', 'CL', 'CO', 'PE' ], 'rateLimit': 1000, 'version': 'v2', 'has': { 'CORS': false, 'createDepositAddress': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFees': true, 'fetchMyTrades': false, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchTrades': true, 'fetchWithdrawals': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/47380619-8a029200-d706-11e8-91e0-8a391fe48de3.jpg', 'api': 'https://www.buda.com/api', 'www': 'https://www.buda.com', 'doc': 'https://api.buda.com', 'fees': 'https://www.buda.com/comisiones', }, 'status': { 'status': 'error', 'updated': undefined, 'eta': undefined, 'url': undefined, }, 'api': { 'public': { 'get': [ 'pairs', 'markets', 'currencies', 'markets/{market}', 'markets/{market}/ticker', 'markets/{market}/volume', 'markets/{market}/order_book', 'markets/{market}/trades', 'currencies/{currency}/fees/deposit', 'currencies/{currency}/fees/withdrawal', 'tv/history', ], 'post': [ 'markets/{market}/quotations', ], }, 'private': { 'get': [ 'balances', 'balances/{currency}', 'currencies/{currency}/balances', 'orders', 'orders/{id}', 'markets/{market}/orders', 'deposits', 'currencies/{currency}/deposits', 'withdrawals', 'currencies/{currency}/withdrawals', 'currencies/{currency}/receive_addresses', 'currencies/{currency}/receive_addresses/{id}', ], 'post': [ 'markets/{market}/orders', 'currencies/{currency}/deposits', 'currencies/{currency}/withdrawals', 'currencies/{currency}/simulated_withdrawals', 'currencies/{currency}/receive_addresses', ], 'put': [ 'orders/{id}', ], }, }, 'timeframes': { '1m': '1', '5m': '5', '30m': '30', '1h': '60', '2h': '120', '1d': 'D', '1w': 'W', }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': 0.008, // 0.8% 'maker': 0.004, // 0.4% 'tiers': { 'taker': [ [0, 0.008], // 0.8% [2000, 0.007], // 0.7% [20000, 0.006], // 0.6% [100000, 0.005], // 0.5% [500000, 0.004], // 0.4% [2500000, 0.003], // 0.3% [12500000, 0.002], // 0.2% ], 'maker': [ [0, 0.004], // 0.4% [2000, 0.0035], // 0.35% [20000, 0.003], // 0.3% [100000, 0.0025], // 0.25% [500000, 0.002], // 0.2% [2500000, 0.0015], // 0.15% [12500000, 0.001], // 0.1% ], }, }, }, 'exceptions': { 'not_authorized': AuthenticationError, // { message: 'Invalid credentials', code: 'not_authorized' } 'forbidden': PermissionDenied, // { message: 'You dont have access to this resource', code: 'forbidden' } 'invalid_record': ExchangeError, // { message: 'Validation Failed', code: 'invalid_record', errors: [] } 'not_found': ExchangeError, // { message: 'Not found', code: 'not_found' } 'parameter_missing': ExchangeError, // { message: 'Parameter missing', code: 'parameter_missing' } 'bad_parameter': ExchangeError, // { message: 'Bad Parameter format', code: 'bad_parameter' } }, }); } async fetchCurrencyInfo (currency, currencies = undefined) { if (!currencies) { const response = await this.publicGetCurrencies (); currencies = this.safeValue (response, 'currencies'); } for (let i = 0; i < currencies.length; i++) { const currencyInfo = currencies[i]; if (currencyInfo['id'] === currency) { return currencyInfo; } } return undefined; } async fetchMarkets (params = {}) { const marketsResponse = await this.publicGetMarkets (params); const markets = this.safeValue (marketsResponse, 'markets'); const currenciesResponse = await this.publicGetCurrencies (); const currencies = this.safeValue (currenciesResponse, 'currencies'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'id'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const baseInfo = await this.fetchCurrencyInfo (baseId, currencies); const quoteInfo = await this.fetchCurrencyInfo (quoteId, currencies); const symbol = base + '/' + quote; const precision = { 'amount': baseInfo['input_decimals'], 'price': quoteInfo['input_decimals'], }; const limits = { 'amount': { 'min': parseFloat (market['minimum_order_amount'][0]), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, }; limits['cost'] = { 'min': limits['amount']['min'] * limits['price']['min'], 'max': undefined, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencies (); const currencies = response['currencies']; const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; if (!currency['managed']) { continue; } const id = this.safeString (currency, 'id'); const code = this.safeCurrencyCode (id); const precision = this.safeFloat (currency, 'input_decimals'); const minimum = Math.pow (10, -precision); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': undefined, 'active': true, 'fee': undefined, 'precision': precision, 'limits': { 'amount': { 'min': minimum, 'max': undefined, }, 'price': { 'min': minimum, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': parseFloat (currency['deposit_minimum'][0]), 'max': undefined, }, 'withdraw': { 'min': parseFloat (currency['withdrawal_minimum'][0]), }, }, }; } return result; } async fetchFundingFees (codes = undefined, params = {}) { // by default it will try load withdrawal fees of all currencies (with separate requests) // however if you define codes = [ 'ETH', 'BTC' ] in args it will only load those await this.loadMarkets (); const withdrawFees = {}; const depositFees = {}; const info = {}; if (codes === undefined) { codes = Object.keys (this.currencies); } for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currency = this.currency (code); const request = { 'currency': currency['id'] }; const withdrawResponse = await this.publicGetCurrenciesCurrencyFeesWithdrawal (request); const depositResponse = await this.publicGetCurrenciesCurrencyFeesDeposit (request); withdrawFees[code] = this.parseFundingFee (withdrawResponse['fee']); depositFees[code] = this.parseFundingFee (depositResponse['fee']); info[code] = { 'withdraw': withdrawResponse, 'deposit': depositResponse, }; } return { 'withdraw': withdrawFees, 'deposit': depositFees, 'info': info, }; } parseFundingFee (fee, type = undefined) { if (type === undefined) { type = fee['name']; } if (type === 'withdrawal') { type = 'withdraw'; } return { 'type': type, 'currency': fee['base'][1], 'rate': fee['percent'], 'cost': parseFloat (fee['base'][0]), }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetMarketsMarketTicker (this.extend (request, params)); const ticker = this.safeValue (response, 'ticker'); return this.parseTicker (ticker, market); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = parseFloat (ticker['last_price'][0]); const percentage = parseFloat (ticker['price_variation_24h']); const open = parseFloat (this.priceToPrecision (symbol, last / (percentage + 1))); const change = last - open; const average = this.sum (last, open) / 2; return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': parseFloat (ticker['max_bid'][0]), 'bidVolume': undefined, 'ask': parseFloat (ticker['min_ask'][0]), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': open, 'change': change, 'percentage': percentage * 100, 'average': average, 'baseVolume': parseFloat (ticker['volume'][0]), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; // the since argument works backwards – returns trades up to the specified timestamp // therefore not implemented here // the method is still available for users to be able to traverse backwards in time // by using the timestamp from the first received trade upon each iteration if (limit !== undefined) { request['limit'] = limit; // 50 max } const response = await this.publicGetMarketsMarketTrades (this.extend (request, params)); // // { trades: { market_id: "ETH-BTC", // timestamp: null, // last_timestamp: "1536901277302", // entries: [ [ "1540077456791", "0.0063767", "0.03", "sell", 479842 ], // [ "1539916642772", "0.01888263", "0.03019563", "sell", 479438 ], // [ "1539834081787", "0.023718648", "0.031001", "sell", 479069 ], // ... ] // return this.parseTrades (response['trades']['entries'], market, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // [ "1540077456791", "0.0063767", "0.03", "sell", 479842 ] // let timestamp = undefined; let side = undefined; const type = undefined; let price = undefined; let amount = undefined; let id = undefined; const order = undefined; const fee = undefined; let symbol = undefined; let cost = undefined; if (market) { symbol = market['symbol']; } if (Array.isArray (trade)) { timestamp = parseInt (trade[0]); price = parseFloat (trade[1]); amount = parseFloat (trade[2]); cost = price * amount; side = trade[3]; id = trade[4].toString (); } return { 'id': id, 'order': order, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetMarketsMarketOrderBook (this.extend (request, params)); const orderbook = this.safeValue (response, 'order_book'); return this.parseOrderBook (orderbook); } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (since === undefined) { since = this.milliseconds () - 86400000; } const request = { 'symbol': market['id'], 'resolution': this.timeframes[timeframe], 'from': since / 1000, 'to': this.seconds (), }; const response = await this.publicGetTvHistory (this.extend (request, params)); return this.parseTradingViewOHLCV (response, market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetBalances (params); const result = { 'info': response }; const balances = this.safeValue (response, 'balances'); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'id'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = parseFloat (balance['available_amount'][0]); account['total'] = parseFloat (balance['amount'][0]); result[code] = account; } return this.parseBalance (result); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': parseInt (id), }; const response = await this.privateGetOrdersId (this.extend (request, params)); const order = this.safeValue (response, 'order'); return this.parseOrder (order); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'market': market['id'], 'per': limit, }; const response = await this.privateGetMarketsMarketOrders (this.extend (request, params)); const orders = this.safeValue (response, 'orders'); return this.parseOrders (orders, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'state': 'pending', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'state': 'traded', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); side = (side === 'buy') ? 'Bid' : 'Ask'; const request = { 'market': this.marketId (symbol), 'price_type': type, 'type': side, 'amount': this.amountToPrecision (symbol, amount), }; if (type === 'limit') { request['limit'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostMarketsMarketOrders (this.extend (request, params)); const order = this.safeValue (response, 'order'); return this.parseOrder (order); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': parseInt (id), 'state': 'canceling', }; const response = await this.privatePutOrdersId (this.extend (request, params)); const order = this.safeValue (response, 'order'); return this.parseOrder (order); } parseOrderStatus (status) { const statuses = { 'traded': 'closed', 'received': 'open', 'canceling': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const id = this.safeString (order, 'id'); const timestamp = this.parse8601 (this.safeString (order, 'created_at')); let symbol = undefined; if (market === undefined) { const marketId = order['market_id']; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } const type = this.safeString (order, 'price_type'); const side = this.safeStringLower (order, 'type'); const status = this.parseOrderStatus (this.safeString (order, 'state')); const amount = parseFloat (order['original_amount'][0]); const remaining = parseFloat (order['amount'][0]); const filled = parseFloat (order['traded_amount'][0]); const cost = parseFloat (order['total_exchanged'][0]); let price = this.safeFloat (order, 'limit'); if (price !== undefined) { price = parseFloat (price[0]); } if (cost > 0 && filled > 0) { price = this.priceToPrecision (symbol, cost / filled); } const fee = { 'cost': parseFloat (order['paid_fee'][0]), 'currency': order['paid_fee'][1], }; return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': fee, 'info': order, 'average': undefined, }; } isFiat (code) { const fiats = { 'ARS': true, 'CLP': true, 'COP': true, 'PEN': true, }; return this.safeValue (fiats, code, false); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); if (this.isFiat (code)) { throw new NotSupported (this.id + ' fetchDepositAddress() for fiat ' + code + ' is not supported'); } const request = { 'currency': currency['id'], }; const response = await this.privateGetCurrenciesCurrencyReceiveAddresses (this.extend (request, params)); const receiveAddresses = this.safeValue (response, 'receive_addresses'); const addressPool = []; for (let i = 1; i < receiveAddresses.length; i++) { const receiveAddress = receiveAddresses[i]; if (receiveAddress['ready']) { const address = receiveAddress['address']; this.checkAddress (address); addressPool.push (address); } } const addressPoolLength = addressPool.length; if (addressPoolLength < 1) { throw new AddressPending (this.id + ': there are no addresses ready for receiving ' + code + ', retry again later)'); } const address = addressPool[0]; return { 'currency': code, 'address': address, 'tag': undefined, 'info': receiveAddresses, }; } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); if (this.isFiat (code)) { throw new NotSupported (this.id + ': fiat fetchDepositAddress() for ' + code + ' is not supported'); } const request = { 'currency': currency['id'], }; const response = await this.privatePostCurrenciesCurrencyReceiveAddresses (this.extend (request, params)); const address = this.safeString (response['receive_address'], 'address'); // the creation is async and returns a null address, returns only the id return { 'currency': code, 'address': address, 'tag': undefined, 'info': response, }; } parseTransactionStatus (status) { const statuses = { 'rejected': 'failed', 'confirmed': 'ok', 'anulled': 'canceled', 'retained': 'canceled', 'pending_confirmation': 'pending', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { const id = this.safeString (transaction, 'id'); const timestamp = this.parse8601 (this.safeString (transaction, 'created_at')); const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const amount = parseFloat (transaction['amount'][0]); const fee = parseFloat (transaction['fee'][0]); const feeCurrency = transaction['fee'][1]; const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); const type = ('deposit_data' in transaction) ? 'deposit' : 'withdrawal'; const data = this.safeValue (transaction, type + '_data', {}); const address = this.safeValue (data, 'target_address'); const txid = this.safeString (data, 'tx_hash'); const updated = this.parse8601 (this.safeString (data, 'updated_at')); return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': updated, 'fee': { 'cost': fee, 'rate': feeCurrency, }, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (code === undefined) { throw new ArgumentsRequired (this.id + ': fetchDeposits() requires a currency code argument'); } const currency = this.currency (code); const request = { 'currency': currency['id'], 'per': limit, }; const response = await this.privateGetCurrenciesCurrencyDeposits (this.extend (request, params)); const deposits = this.safeValue (response, 'deposits'); return this.parseTransactions (deposits, currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (code === undefined) { throw new ArgumentsRequired (this.id + ': fetchDeposits() requires a currency code argument'); } const currency = this.currency (code); const request = { 'currency': currency['id'], 'per': limit, }; const response = await this.privateGetCurrenciesCurrencyWithdrawals (this.extend (request, params)); const withdrawals = this.safeValue (response, 'withdrawals'); return this.parseTransactions (withdrawals, currency, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'amount': amount, 'withdrawal_data': { 'target_address': address, }, }; const response = await this.privatePostCurrenciesCurrencyWithdrawals (this.extend (request, params)); const withdrawal = this.safeValue (response, 'withdrawal'); return this.parseTransaction (withdrawal); } nonce () { return this.microseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (Object.keys (query).length) { if (method === 'GET') { request += '?' + this.urlencode (query); } else { body = this.json (query); } } const url = this.urls['api'] + '/' + this.version + '/' + request; if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const components = [ method, '/api/' + this.version + '/' + request ]; if (body) { const base64Body = this.stringToBase64 (this.encode (body)); components.push (this.decode (base64Body)); } components.push (nonce); const message = components.join (' '); const signature = this.hmac (this.encode (message), this.encode (this.secret), 'sha384'); headers = { 'X-SBTC-APIKEY': this.apiKey, 'X-SBTC-SIGNATURE': signature, 'X-SBTC-NONCE': nonce, 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if (code >= 400) { const errorCode = this.safeString (response, 'code'); const message = this.safeString (response, 'message', body); const feedback = this.id + ' ' + message; if (errorCode !== undefined) { this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9}],55:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { RateLimitExceeded, BadSymbol, OrderNotFound, ExchangeError, AuthenticationError, ArgumentsRequired, ExchangeNotAvailable } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bw extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bw', 'name': 'BW', 'countries': [ 'CN' ], 'rateLimit': 1500, 'version': 'v1', 'has': { 'cancelAllOrders': false, 'cancelOrder': true, 'cancelOrders': false, 'CORS': false, 'createDepositAddress': false, 'createLimitOrder': true, 'createMarketOrder': false, 'createOrder': true, 'deposit': false, 'editOrder': false, 'fetchBalance': true, 'fetchBidsAsks': false, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFees': false, 'fetchL2OrderBook': false, 'fetchLedger': false, 'fetchMarkets': true, 'fetchMyTrades': false, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrderBooks': false, 'fetchOrders': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTradingLimits': false, 'fetchTransactions': false, 'fetchWithdrawals': true, 'privateAPI': false, 'publicAPI': false, 'withdraw': false, }, 'timeframes': { '1m': '1M', '5m': '5M', '15m': '15M', '30m': '30M', '1h': '1H', '1w': '1W', }, 'hostname': 'bw.com', // set to 'bw.io' for China mainland 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/69436317-31128c80-0d52-11ea-91d1-eb7bb5818812.jpg', 'api': 'https://www.{hostname}', 'www': 'https://www.bw.com', 'doc': 'https://github.com/bw-exchange/api_docs_en/wiki', 'fees': 'https://www.bw.com/feesRate', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.2 / 100, 'maker': 0.2 / 100, }, 'funding': { }, }, 'exceptions': { 'exact': { '999': AuthenticationError, '1000': ExchangeNotAvailable, // {"datas":null,"resMsg":{"message":"getKlines error:data not exitsts\uff0cplease wait ,dataType=4002_KLINE_1M","method":null,"code":"1000"}} '2012': OrderNotFound, // {"datas":null,"resMsg":{"message":"entrust not exists or on dealing with system","method":null,"code":"2012"}} '5017': BadSymbol, // {"datas":null,"resMsg":{"message":"market not exist","method":null,"code":"5017"}} '10001': RateLimitExceeded, // {"resMsg":{"code":"10001","message":"API frequency limit"}} }, }, 'api': { 'public': { 'get': [ 'api/data/v1/klines', 'api/data/v1/ticker', 'api/data/v1/tickers', 'api/data/v1/trades', 'api/data/v1/entrusts', 'exchange/config/controller/website/marketcontroller/getByWebId', 'exchange/config/controller/website/currencycontroller/getCurrencyList', ], }, 'private': { 'get': [ 'exchange/entrust/controller/website/EntrustController/getEntrustById', 'exchange/entrust/controller/website/EntrustController/getUserEntrustRecordFromCacheWithPage', 'exchange/entrust/controller/website/EntrustController/getUserEntrustList', 'exchange/fund/controller/website/fundwebsitecontroller/getwithdrawaddress', 'exchange/fund/controller/website/fundwebsitecontroller/getpayoutcoinrecord', 'exchange/entrust/controller/website/EntrustController/getUserEntrustList', // the docs say that the following URLs are HTTP POST // in the docs header and HTTP GET in the docs body // the docs contradict themselves, a typo most likely // the actual HTTP method is POST for this endpoint // 'exchange/fund/controller/website/fundcontroller/getPayinAddress', // 'exchange/fund/controller/website/fundcontroller/getPayinCoinRecord', ], 'post': [ 'exchange/fund/controller/website/fundcontroller/getPayinAddress', // see the comment above 'exchange/fund/controller/website/fundcontroller/getPayinCoinRecord', // see the comment above 'exchange/fund/controller/website/fundcontroller/findbypage', 'exchange/entrust/controller/website/EntrustController/addEntrust', 'exchange/entrust/controller/website/EntrustController/cancelEntrust', ], }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetExchangeConfigControllerWebsiteMarketcontrollerGetByWebId (params); // // { // "datas": [ // { // "orderNum":null, // "leverEnable":true, // "leverMultiple":10, // "marketId":"291", // "webId":"102", // "serverId":"entrust_bw_23", // "name":"eos_usdt", // "leverType":"2", // "buyerCurrencyId":"11", // "sellerCurrencyId":"7", // "amountDecimal":4, // "priceDecimal":3, // "minAmount":"0.0100000000", // "state":1, // "openTime":1572537600000, // "defaultFee":"0.00200000", // "createUid":null, // "createTime":0, // "modifyUid":null, // "modifyTime":1574160113735, // "combineMarketId":"", // "isCombine":0, // "isMining":0 // } // ], // "resMsg": { "message":"success !", "method":null, "code":"1" } // } // const markets = this.safeValue (response, 'datas', []); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'marketId'); const numericId = parseInt (id); const name = this.safeStringUpper (market, 'name'); let [ base, quote ] = name.split ('_'); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const baseId = this.safeString (market, 'sellerCurrencyId'); const quoteId = this.safeString (market, 'buyerCurrencyId'); const baseNumericId = parseInt (baseId); const quoteNumericId = parseInt (quoteId); const symbol = base + '/' + quote; const state = this.safeInteger (market, 'state'); const active = (state === 1); const fee = this.safeFloat (market, 'defaultFee'); result.push ({ 'id': id, 'active': active, 'numericId': numericId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'baseNumericId': baseNumericId, 'quoteNumericId': quoteNumericId, 'maker': fee, 'taker': fee, 'info': market, 'precision': { 'amount': this.safeInteger (market, 'amountDecimal'), 'price': this.safeInteger (market, 'priceDecimal'), }, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minAmount'), 'max': undefined, }, 'price': { 'min': 0, 'max': undefined, }, 'cost': { 'min': 0, 'max': undefined, }, }, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetExchangeConfigControllerWebsiteCurrencycontrollerGetCurrencyList (params); // // { // "datas":[ // { // "currencyId":"456", // "name":"pan", // "alias":"pan", // "logo":"pan.svg", // "description":"pan", // "descriptionEnglish":"pan", // "defaultDecimal":2, // "createUid":null, // "createTime":1574068133762, // "modifyUid":null, // "modifyTime":0, // "state":1, // "mark":"pan", // "totalNumber":"0", // "publishNumber":"0", // "marketValue":"0", // "isLegalCoin":0, // "needBlockUrl":1, // "blockChainUrl":"https://etherscan.io/tx/", // "tradeSearchUrl":null, // "tokenCoinsId":0, // "isMining":"0", // "arithmetic":null, // "founder":"bw_nxwal", // "teamAddress":null, // "remark":null, // "tokenName":"ethw2", // "isMemo":0, // "websiteCurrencyId":"7rhqoHLohkG", // "drawFlag":0, // "rechargeFlag":1, // "drawFee":"0.03000000", // "onceDrawLimit":100, // "dailyDrawLimit":500, // "timesFreetrial":"0", // "hourFreetrial":"0", // "dayFreetrial":"0", // "minFee":"0", // "inConfigTimes":7, // "outConfigTimes":7, // "minCash":"0.06000000", // "limitAmount":"0", // "zbExist":false, // "zone":1 // }, // ], // "resMsg": { "message":"success !", "method":null, "code":"1" } // } // const currencies = this.safeValue (response, 'datas', []); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'currencyId'); const code = this.safeCurrencyCode (this.safeStringUpper (currency, 'name')); const state = this.safeInteger (currency, 'state'); const active = state === 1; result[code] = { 'id': id, 'code': code, 'info': currency, 'name': code, 'active': active, 'fee': this.safeFloat (currency, 'drawFee'), 'precision': undefined, 'limits': { 'amount': { 'min': this.safeFloat (currency, 'limitAmount', 0), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': this.safeFloat (currency, 'onceDrawLimit'), }, }, }; } return result; } parseTicker (ticker, market = undefined) { // // [ // "281", // market id // "9754.4", // last // "9968.8", // high // "9631.5", // low // "47865.6432", // base volume // "-2.28", // change // // closing price for last 6 hours // "[[1, 9750.1], [2, 9737.1], [3, 9727.5], [4, 9722], [5, 9722.1], [6, 9754.4]]", // "9752.12", // bid // "9756.69", // ask // "469849357.2364" // quote volume // ] // let symbol = undefined; const marketId = this.safeString (ticker, 0); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if (market !== undefined) { symbol = market['symbol']; } else { symbol = marketId; } const timestamp = this.milliseconds (); const close = parseFloat (this.safeValue (ticker, 1)); const bid = this.safeValue (ticker, 'bid', {}); const ask = this.safeValue (ticker, 'ask', {}); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': parseFloat (this.safeValue (ticker, 2)), 'low': parseFloat (this.safeValue (ticker, 3)), 'bid': parseFloat (this.safeValue (ticker, 7)), 'bidVolume': this.safeFloat (bid, 'quantity'), 'ask': parseFloat (this.safeValue (ticker, 8)), 'askVolume': this.safeFloat (ask, 'quantity'), 'vwap': undefined, 'open': undefined, 'close': close, 'last': close, 'previousClose': undefined, 'change': parseFloat (this.safeValue (ticker, 5)), 'percentage': undefined, 'average': undefined, 'baseVolume': parseFloat (this.safeValue (ticker, 4)), 'quoteVolume': parseFloat (this.safeValue (ticker, 9)), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], }; const response = await this.publicGetApiDataV1Ticker (this.extend (request, params)); // // { // "datas": [ // "281", // "7601.99", // "8126.5", // "7474.68", // "47004.8708", // "-6.18", // "[[1, 7800.34], [2, 7626.41], [3, 7609.97], [4, 7569.04], [5, 7577.93], [6, 7601.99]]", // "7600.24", // "7603.69", // "371968300.0119", // ], // "resMsg": { "message": "success !", "method": null, "code": "1" } // } // const ticker = this.safeValue (response, 'datas', []); return this.parseTicker (ticker, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetApiDataV1Tickers (params); // // { // "datas": [ // [ // "4051", // "0.00194", // "0.00863", // "0.0012", // "1519020", // "-38.22", // "[[1, 0.0023], [2, 0.00198], [3, 0.00199], [4, 0.00195], [5, 0.00199], [6, 0.00194]]", // "0.00123", // "0.0045", // "4466.8104", // ], // ], // "resMsg": { "message": "success !", "method": null, "code": "1" }, // } // const datas = this.safeValue (response, 'datas', []); const result = {}; for (let i = 0; i < datas.length; i++) { const ticker = this.parseTicker (datas[i]); const symbol = ticker['symbol']; if ((symbols === undefined) || this.inArray (symbol, symbols)) { result[symbol] = ticker; } } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], }; if (limit !== undefined) { request['dataSize'] = limit; } const response = await this.publicGetApiDataV1Entrusts (this.extend (request, params)); // // { // "datas": { // "asks": [ // [ "9740.43", "0.0083" ], // ], // "bids": [ // [ "9734.33", "0.0133" ], // ], // "timestamp": "1569303520", // }, // "resMsg": { // "message": "success !", // "method": null, // "code": "1", // }, // } // const orderbook = this.safeValue (response, 'datas', []); const timestamp = this.safeTimestamp (orderbook, 'timestamp'); return this.parseOrderBook (orderbook, timestamp); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // [ // "T", // trade // "281", // market id // "1569303302", // timestamp // "BTC_USDT", // market name // "ask", // side // "9745.08", // price // "0.0026" // amount // ] // // fetchMyTrades (private) // // ... // const timestamp = this.safeTimestamp (trade, 2); const price = this.safeFloat (trade, 5); const amount = this.safeFloat (trade, 6); const marketId = this.safeString (trade, 1); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const marketName = this.safeString (trade, 3); const [ baseId, quoteId ] = marketName.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = this.costToPrecision (symbol, price * amount); } } const sideString = this.safeString (trade, 4); const side = (sideString === 'ask') ? 'sell' : 'buy'; return { 'id': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': 'limit', 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': parseFloat (cost), 'fee': undefined, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], }; if (limit !== undefined) { request['dataSize'] = limit; // max 20 } const response = await this.publicGetApiDataV1Trades (this.extend (request, params)); // // { // "datas": [ // [ // "T", // trade // "281", // market id // "1569303302", // timestamp // "BTC_USDT", // market name // "ask", // side // "9745.08", // price // "0.0026" // amount // ], // ], // "resMsg": { "code": "1", "method": null, "message": "success !" }, // } // const trades = this.safeValue (response, 'datas', []); return this.parseTrades (trades, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ this.safeTimestamp (ohlcv, 3), this.safeFloat (ohlcv, 4), this.safeFloat (ohlcv, 5), this.safeFloat (ohlcv, 6), this.safeFloat (ohlcv, 7), this.safeFloat (ohlcv, 8), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], 'type': this.timeframes[timeframe], 'dataSize': 500, }; if (limit !== undefined) { request['dataSize'] = limit; } const response = await this.publicGetApiDataV1Klines (this.extend (request, params)); const data = this.safeValue (response, 'datas', []); const ohlcvs = this.parseOHLCVs (data, market, timeframe, since, limit); return this.sortBy (ohlcvs, 0); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerFindbypage (params); // // { // "datas": { // "totalRow": 6, // "pageSize": 99, // "list": [ // { // "amount": "0.000090000000000000", // The current number of tokens available // "currencyTypeId": 2, // Token ID // "freeze": "0.009900000000000000", // Current token freezing quantity // }, // ], // "pageNum": 1, // }, // "resMsg": { "code": "1", "message": "success !" } // } // const data = this.safeValue (response, 'datas', {}); const balances = this.safeValue (data, 'list', []); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currencyTypeId'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'amount'); account['used'] = this.safeFloat (balance, 'freeze'); result[code] = account; } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (price === undefined) { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'amount': this.amountToPrecision (symbol, amount), 'price': this.priceToPrecision (symbol, price), 'type': (side === 'buy') ? 1 : 0, 'rangeType': 0, // limit order 'marketId': market['id'], }; const response = await this.privatePostExchangeEntrustControllerWebsiteEntrustControllerAddEntrust (this.extend (request, params)); // // { // "datas": { // "entrustId": "E6581105708337483776", // }, // "resMsg": { // "message": "success !", // "method": null, // "code": "1" // } // } // const data = this.safeValue (response, 'datas'); const id = this.safeString (data, 'entrustId'); return { 'id': id, 'info': response, 'timestamp': undefined, 'datetime': undefined, 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': undefined, 'average': undefined, 'filled': undefined, 'remaining': undefined, 'status': 'open', 'fee': undefined, 'trades': undefined, 'clientOrderId': undefined, }; } parseOrderStatus (status) { const statuses = { '-3': 'canceled', '-2': 'canceled', '-1': 'canceled', '0': 'open', '1': 'canceled', '2': 'closed', '3': 'open', '4': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // fetchOrder, fetchOpenOrders, fetchClosedOrders // // { // "entrustId": "E6581108027628212224", // Order id // "price": "1450", // price // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission // "amount": "14.05", // Order quantity // "totalMoney": "20372.50", // Total order amount // "completeAmount": "0", // Quantity sold // "completeTotalMoney": "0", // Total dealt amount // "type": 1, // 0 = sell, 1 = buy, -1 = cancel // "entrustType": 0, // 0 = ordinary current price commission, 1 = lever commission // "status": 0, // // "marketId": "318", // The market id // "createTime": 1569058424861, // Create time // "availabelAmount": "14.05" // Outstanding quantity, typo in the docs or in the API, availabel vs available // } // const marketId = this.safeString (order, 'marketId'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } const timestamp = this.safeInteger (order, 'createTime'); let side = this.safeString (order, 'type'); if (side === '0') { side = 'sell'; } else if (side === '1') { side = 'buy'; } const amount = this.safeFloat (order, 'amount'); const price = this.safeFloat (order, 'price'); const filled = this.safeFloat (order, 'completeAmount'); let remaining = this.safeFloat2 (order, 'availabelAmount', 'availableAmount'); // typo in the docs or in the API, availabel vs available let cost = this.safeFloat (order, 'totalMoney'); if (filled !== undefined) { if (amount !== undefined) { if (remaining === undefined) { remaining = amount - filled; } } if (cost === undefined) { if (price !== undefined) { cost = filled * cost; } } } const status = this.parseOrderStatus (this.safeString (order, 'status')); return { 'info': order, 'id': this.safeString (order, 'entrustId'), 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': this.safeString (market, 'symbol'), 'type': 'limit', 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': undefined, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], 'entrustId': id, }; const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetEntrustById (this.extend (request, params)); // // { // "datas": { // "entrustId": "E6581108027628212224", // Order id // "price": "1450", // price // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission // "amount": "14.05", // Order quantity // "totalMoney": "20372.50", // Total order amount // "completeAmount": "0", // Quantity sold // "completeTotalMoney": "0", // Total dealt amount // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission // "status": 0, // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt // "marketId": "318", // The market id // "createTime": 1569058424861, // Create time // "availabelAmount": "14.05" // Outstanding quantity // }, // "resMsg": { "message": "success !", "method": null, "code": "1" } // } // const order = this.safeValue (response, 'datas', {}); return this.parseOrder (order, market); } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], 'entrustId': id, }; const response = await this.privatePostExchangeEntrustControllerWebsiteEntrustControllerCancelEntrust (this.extend (request, params)); // // { // "datas": null, // "resMsg": { "message": "success !", "method": null, "code": "1" } // } // return { 'info': response, 'id': id, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], // 'pageSize': limit, // documented as required, but it works without it // 'pageIndex': 0, // also works without it, most likely a typo in the docs }; if (limit !== undefined) { request['pageSize'] = limit; // default limit is 20 } const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustRecordFromCacheWithPage (this.extend (request, params)); // // { // "datas": { // "pageNum": 1, // "pageSize": 2, // "totalPage": 20, // "totalRow": 40, // "entrustList": [ // { // "amount": "14.050000000000000000", // Order quantity // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission // "totalMoney": "20372.500000000000000000", // Total order amount // "entrustId": "E6581108027628212224", // Order id // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel // "completeAmount": "0", // Quantity sold // "marketId": "318", // The market id // "createTime": 1569058424861, // Create time // "price": "1450.000000000", // price // "completeTotalMoney": "0", // Quantity sold // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission // "status": 0 // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt // }, // ], // }, // "resMsg": { "message": "success !", "method": null, "code": "1" }, // } // const data = this.safeValue (response, 'datas', {}); const orders = this.safeValue (data, 'entrustList', []); return this.parseOrders (orders, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], }; if (limit !== undefined) { request['pageSize'] = limit; // default limit is 20 } if (since !== undefined) { request['startDateTime'] = since; } const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustList (this.extend (request, params)); const data = this.safeValue (response, 'datas', {}); const orders = this.safeValue (data, 'entrustList', []); return this.parseOrders (orders, market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], // 'pageSize': limit, // documented as required, but it works without it // 'pageIndex': 0, // also works without it, most likely a typo in the docs // 'type': 0, // 0 = sell, 1 = buy, -1 = cancel // 'status': -1, // -1 = insufficient funds, failed orders, 0 = pending orders, 1 = canceled, 2 = closed, 3 = partial // 'startDateTime': since, // 'endDateTime': this.milliseconds (), }; if (since !== undefined) { request['startDateTime'] = since; } if (limit !== undefined) { request['pageSize'] = limit; // default limit is 20 } const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustList (this.extend (request, params)); // // { // "datas": { // "pageNum": 1, // "pageSize": 2, // "totalPage": 20, // "totalRow": 40, // "entrustList": [ // { // "amount": "14.050000000000000000", // Order quantity // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission // "totalMoney": "20372.500000000000000000", // Total order amount // "entrustId": "E6581108027628212224", // Order id // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel // "completeAmount": "0", // Quantity sold // "marketId": "318", // The market id // "createTime": 1569058424861, // Create time // "price": "1450.000000000", // price // "completeTotalMoney": "0", // Quantity sold // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission // "status": 0 // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt // }, // ], // }, // "resMsg": { "message": "success !", "method": null, "code": "1" }, // } // const data = this.safeValue (response, 'datas', {}); const orders = this.safeValue (data, 'entrustList', []); return this.parseOrders (orders, market, since, limit); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.implodeParams (this.urls['api'], { 'hostname': this.hostname }) + '/' + path; if (method === 'GET') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { body = this.json (params); } if (api === 'private') { const ms = this.milliseconds ().toString (); let content = ''; if (method === 'GET') { const sortedParams = this.keysort (params); const keys = Object.keys (sortedParams); for (let i = 0; i < keys.length; i++) { const key = keys[i]; content += key + sortedParams[key].toString (); } } else { content = body; } const signature = this.apiKey + ms + content + this.secret; const hash = this.hash (this.encode (signature), 'md5'); if (!headers) { headers = {}; } headers['Apiid'] = this.apiKey; headers['Timestamp'] = ms; headers['Sign'] = hash; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currencyTypeName': currency['name'], }; const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerGetPayinAddress (this.extend (request, params)); // // { // "datas": { // "isMemo": true, // 是否为memo 格式,false:否,true :是 // "address": "bweosdeposit_787928102918558272", // 充币地址 // "memo": "787928102918558272", // 币种memo // "account": "bweosdeposit" // 币种账户 // }, // "resMsg": { "message": "success !", "method": null, "code": "1" } // } // const data = this.safeValue (response, 'datas', {}); const address = this.safeString (data, 'address'); const tag = this.safeString (data, 'memo'); this.checkAddress (address); return { 'currency': code, 'address': this.checkAddress (address), 'tag': tag, 'info': response, }; } parseTransactionStatus (status) { const statuses = { '-1': 'canceled', // or auditing failed '0': 'pending', '1': 'ok', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // "depositId": "D6574268549744189441", // Deposit ID // "amount": "54.753589700000000000", // Deposit amount // "txId": "INNER_SYSTEM_TRANSFER_1198941", // Trading ID // "confirmTimes": 0, // Confirmation number // "depositAddress": "bweosdeposit_787928102918558272", // Deposit address // "createTime": "2019-09-02 20:36:08.0", // Deposit time // "status": 1, // Deposit status, 0: not received, 1: received // "currencyTypeId": 7, // Token ID // } // // fetchWithdrawals // // { // "withdrawalId": "W6527498439872634880", // Withdrawal ID // "fees": "0.500000000000000000", // Withdrawal fee // "withdrawalAddress": "okbtothemoon_941657", // Withdrawal address // "currencyId": "7", // Token ID // "amount": "10.000000000000000000", // Withdrawal amount // "state": 1, // Status, 1: normal, -1: delete // "verifyStatus": 1, // Audit status, 0: to be audited, 1: auditing passed, -1: auditing failed // "createTime": 1556276903656, // WIthdrawal time // "actuallyAmount": "9.500000000000000000", // Actual amount received // } // const id = this.safeString (transaction, 'depositId', 'withdrawalId'); const address = this.safeString2 (transaction, 'depositAddress', 'withdrawalAddress'); const currencyId = this.safeString2 (transaction, 'currencyId', 'currencyTypeId'); let code = undefined; if (currencyId in this.currencies_by_id) { currency = this.currencies_by_id[currencyId]; } if ((code === undefined) && (currency !== undefined)) { code = currency['code']; } const type = ('depositId' in transaction) ? 'deposit' : 'withdrawal'; const amount = this.safeFloat2 (transaction, 'actuallyAmount', 'amount'); const status = this.parseTransactionStatus (this.safeString2 (transaction, 'verifyStatus', 'state')); const timestamp = this.safeInteger (transaction, 'createTime'); const txid = this.safeString (transaction, 'txId'); let fee = undefined; const feeCost = this.safeFloat (transaction, 'fees'); if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': code, }; } return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'addressFrom': undefined, 'address': address, 'addressTo': undefined, 'tagFrom': undefined, 'tag': undefined, 'tagTo': undefined, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': fee, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currencyTypeName': currency['name'], // 'pageSize': limit, // documented as required, but it works without it // 'pageNum': 0, // also works without it, most likely a typo in the docs // 'sort': 1, // 1 = asc, 0 = desc }; if (limit !== undefined) { request['pageSize'] = limit; // default 50 } const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerGetPayinCoinRecord (this.extend (request, params)); // // { // "datas": { // "totalRow":2, // "totalPage": 1, // "pageSize": 2, // "pageNum": 1, // "list": [ // { // "depositId": "D6574268549744189441", // Deposit ID // "amount": "54.753589700000000000", // Deposit amount // "txId": "INNER_SYSTEM_TRANSFER_1198941", // Trading ID // "confirmTimes": 0, // Confirmation number // "depositAddress": "bweosdeposit_787928102918558272", // Deposit address // "createTime": "2019-09-02 20:36:08.0", // Deposit time // "status": 1, // Deposit status, 0: not received, 1: received // "currencyTypeId": 7, // Token ID // }, // ] // }, // "resMsg": { "message": "success !", "method": null, "code": "1" }, // } // const data = this.safeValue (response, 'datas', {}); const deposits = this.safeValue (data, 'list', []); return this.parseTransactions (deposits, code, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currencyId': currency['id'], // 'pageSize': limit, // documented as required, but it works without it // 'pageIndex': 0, // also works without it, most likely a typo in the docs // 'tab': 'all', // all, wait (submitted, not audited), success (auditing passed), fail (auditing failed), cancel (canceled by user) }; if (limit !== undefined) { request['pageSize'] = limit; // default 50 } const response = await this.privateGetExchangeFundControllerWebsiteFundwebsitecontrollerGetpayoutcoinrecord (this.extend (request, params)); // // { // "datas": { // "totalRow": 1, // "totalPage": 1, // "pageSize": 2, // "pageNum": 1, // "list": [ // { // "withdrawalId": "W6527498439872634880", // Withdrawal ID // "fees": "0.500000000000000000", // Withdrawal fee // "withdrawalAddress": "okbtothemoon_941657", // Withdrawal address // "currencyId": "7", // Token ID // "amount": "10.000000000000000000", // Withdrawal amount // "state": 1, // Status, 1: normal, -1: delete // "verifyStatus": 1, // Audit status, 0: to be audited, 1: auditing passed, -1: auditing failed // "createTime": 1556276903656, // WIthdrawal time // "actuallyAmount": "9.500000000000000000", // Actual amount received // }, // ], // }, // "resMsg": { "message": "success !", "method": null, "code": "1" }, // } // const data = this.safeValue (response, 'datas', {}); const withdrawals = this.safeValue (data, 'list', []); return this.parseTransactions (withdrawals, code, since, limit); } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (!response) { return; // default error handler } const resMsg = this.safeValue (response, 'resMsg'); const errorCode = this.safeString (resMsg, 'code'); if (errorCode !== '1') { const feedback = this.id + ' ' + this.json (response); this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); throw new ExchangeError (feedback); // unknown error } } }; },{"./base/Exchange":7,"./base/errors":9}],56:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { TICK_SIZE } = require ('./base/functions/number'); const { AuthenticationError, ExchangeError, ArgumentsRequired, PermissionDenied, InvalidOrder, OrderNotFound, InsufficientFunds, BadRequest, RateLimitExceeded, InvalidNonce, NotSupported } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class bybit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bybit', 'name': 'Bybit', 'countries': [ 'VG' ], // British Virgin Islands 'version': 'v2', 'userAgent': undefined, 'rateLimit': 100, 'has': { 'CORS': true, 'fetchMarkets': true, 'fetchBalance': true, 'fetchOHLCV': true, 'editOrder': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchOrderTrades': true, 'createOrder': true, 'cancelOrder': true, 'cancelAllOrders': true, 'fetchTime': true, 'fetchWithdrawals': true, 'fetchDeposits': false, 'fetchTransactions': false, 'fetchLedger': true, }, 'timeframes': { '1m': '1', '3m': '3', '5m': '5', '15m': '15', '30m': '30', '1h': '60', '2h': '120', '4h': '240', '6h': '360', '12h': '720', '1d': 'D', '1w': 'W', '1M': 'M', '1y': 'Y', }, 'urls': { 'test': 'https://api-testnet.bybit.com', 'logo': 'https://user-images.githubusercontent.com/51840849/76547799-daff5b80-649e-11ea-87fb-3be9bac08954.jpg', 'api': 'https://api.bybit.com', 'www': 'https://www.bybit.com/', 'doc': [ 'https://bybit-exchange.github.io/docs/inverse/', 'https://bybit-exchange.github.io/docs/linear/', 'https://github.com/bybit-exchange', ], 'fees': 'https://help.bybit.com/hc/en-us/articles/360039261154', 'referral': 'https://www.bybit.com/app/register?ref=X7Prm', }, 'api': { 'public': { 'get': [ 'orderBook/L2', 'kline/list', 'tickers', 'trading-records', 'symbols', 'time', 'announcement', ], }, 'private': { 'get': [ 'order', 'stop-order', 'position/list', 'wallet/balance', 'execution/list', ], 'post': [ 'order/create', 'order/cancel', 'order/cancelAll', 'stop-order/cancelAll', ], }, 'openapi': { 'get': [ 'order/list', 'stop-order/list', 'wallet/risk-limit/list', 'wallet/risk-limit', 'funding/prev-funding-rate', 'funding/prev-funding', 'funding/predicted-funding', 'api-key', 'wallet/fund/records', 'wallet/withdraw/list', ], 'post': [ 'order/replace', 'stop-order/create', 'stop-order/cancel', 'stop-order/replace', 'position/trading-stop', ], }, 'publicLinear': { 'get': [ 'kline', 'recent-trading-records', 'funding/prev-funding-rate', 'mark-price-kline', ], }, 'privateLinear': { 'get': [ 'order/list', 'order/search', 'stop-order/list', 'stop-order/search', 'position/list', 'position/set-auto-add-margin', 'position/set-leverage', 'position/switch-isolated', 'position/trading-stop', 'position/add-margin', 'trade/execution/list', 'trade/closed-pnl/list', 'risk-limit', 'funding/prev-funding', 'funding/predicted-funding', ], 'post': [ 'order/create', 'order/cancel', 'order/cancelAll', 'stop-order/create', 'stop-order/cancel', 'stop-order/cancelAll', ], }, 'position': { 'post': [ 'change-position-margin', ], }, 'user': { 'get': [ 'leverage', ], 'post': [ 'leverage/save', ], }, }, 'httpExceptions': { '403': RateLimitExceeded, // Forbidden -- You request too many times }, 'exceptions': { 'exact': { '10001': BadRequest, // parameter error '10002': InvalidNonce, // request expired, check your timestamp and recv_window '10003': AuthenticationError, // Invalid apikey '10004': AuthenticationError, // invalid sign '10005': PermissionDenied, // permission denied for current apikey '10006': RateLimitExceeded, // too many requests '10007': AuthenticationError, // api_key not found in your request parameters '10010': PermissionDenied, // request ip mismatch '10017': BadRequest, // request path not found or request method is invalid '20001': OrderNotFound, // Order not exists '20003': InvalidOrder, // missing parameter side '20004': InvalidOrder, // invalid parameter side '20005': InvalidOrder, // missing parameter symbol '20006': InvalidOrder, // invalid parameter symbol '20007': InvalidOrder, // missing parameter order_type '20008': InvalidOrder, // invalid parameter order_type '20009': InvalidOrder, // missing parameter qty '20010': InvalidOrder, // qty must be greater than 0 '20011': InvalidOrder, // qty must be an integer '20012': InvalidOrder, // qty must be greater than zero and less than 1 million '20013': InvalidOrder, // missing parameter price '20014': InvalidOrder, // price must be greater than 0 '20015': InvalidOrder, // missing parameter time_in_force '20016': InvalidOrder, // invalid value for parameter time_in_force '20017': InvalidOrder, // missing parameter order_id '20018': InvalidOrder, // invalid date format '20019': InvalidOrder, // missing parameter stop_px '20020': InvalidOrder, // missing parameter base_price '20021': InvalidOrder, // missing parameter stop_order_id '20022': BadRequest, // missing parameter leverage '20023': BadRequest, // leverage must be a number '20031': BadRequest, // leverage must be greater than zero '20070': BadRequest, // missing parameter margin '20071': BadRequest, // margin must be greater than zero '20084': BadRequest, // order_id or order_link_id is required '30001': BadRequest, // order_link_id is repeated '30003': InvalidOrder, // qty must be more than the minimum allowed '30004': InvalidOrder, // qty must be less than the maximum allowed '30005': InvalidOrder, // price exceeds maximum allowed '30007': InvalidOrder, // price exceeds minimum allowed '30008': InvalidOrder, // invalid order_type '30009': ExchangeError, // no position found '30010': InsufficientFunds, // insufficient wallet balance '30011': PermissionDenied, // operation not allowed as position is undergoing liquidation '30012': PermissionDenied, // operation not allowed as position is undergoing ADL '30013': PermissionDenied, // position is in liq or adl status '30014': InvalidOrder, // invalid closing order, qty should not greater than size '30015': InvalidOrder, // invalid closing order, side should be opposite '30016': ExchangeError, // TS and SL must be cancelled first while closing position '30017': InvalidOrder, // estimated fill price cannot be lower than current Buy liq_price '30018': InvalidOrder, // estimated fill price cannot be higher than current Sell liq_price '30019': InvalidOrder, // cannot attach TP/SL params for non-zero position when placing non-opening position order '30020': InvalidOrder, // position already has TP/SL params '30021': InvalidOrder, // cannot afford estimated position_margin '30022': InvalidOrder, // estimated buy liq_price cannot be higher than current mark_price '30023': InvalidOrder, // estimated sell liq_price cannot be lower than current mark_price '30024': InvalidOrder, // cannot set TP/SL/TS for zero-position '30025': InvalidOrder, // trigger price should bigger than 10% of last price '30026': InvalidOrder, // price too high '30027': InvalidOrder, // price set for Take profit should be higher than Last Traded Price '30028': InvalidOrder, // price set for Stop loss should be between Liquidation price and Last Traded Price '30029': InvalidOrder, // price set for Stop loss should be between Last Traded Price and Liquidation price '30030': InvalidOrder, // price set for Take profit should be lower than Last Traded Price '30031': InsufficientFunds, // insufficient available balance for order cost '30032': InvalidOrder, // order has been filled or cancelled '30033': RateLimitExceeded, // The number of stop orders exceeds maximum limit allowed '30034': OrderNotFound, // no order found '30035': RateLimitExceeded, // too fast to cancel '30036': ExchangeError, // the expected position value after order execution exceeds the current risk limit '30037': InvalidOrder, // order already cancelled '30041': ExchangeError, // no position found '30042': InsufficientFunds, // insufficient wallet balance '30043': PermissionDenied, // operation not allowed as position is undergoing liquidation '30044': PermissionDenied, // operation not allowed as position is undergoing AD '30045': PermissionDenied, // operation not allowed as position is not normal status '30049': InsufficientFunds, // insufficient available balance '30050': ExchangeError, // any adjustments made will trigger immediate liquidation '30051': ExchangeError, // due to risk limit, cannot adjust leverage '30052': ExchangeError, // leverage can not less than 1 '30054': ExchangeError, // position margin is invalid '30057': ExchangeError, // requested quantity of contracts exceeds risk limit '30063': ExchangeError, // reduce-only rule not satisfied '30067': InsufficientFunds, // insufficient available balance '30068': ExchangeError, // exit value must be positive '34026': ExchangeError, // the limit is no change }, 'broad': { 'unknown orderInfo': OrderNotFound, // {"ret_code":-1,"ret_msg":"unknown orderInfo","ext_code":"","ext_info":"","result":null,"time_now":"1584030414.005545","rate_limit_status":99,"rate_limit_reset_ms":1584030414003,"rate_limit":100} }, }, 'precisionMode': TICK_SIZE, 'options': { 'marketTypes': { 'BTC/USDT': 'linear', }, 'code': 'BTC', 'fetchBalance': { 'code': 'BTC', }, 'cancelAllOrders': { 'method': 'privatePostOrderCancelAll', // privatePostStopOrderCancelAll }, 'recvWindow': 5 * 1000, // 5 sec default 'timeDifference': 0, // the difference between system clock and Binance clock 'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.00075, 'maker': -0.00025, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, }); } nonce () { return this.milliseconds () - this.options['timeDifference']; } async loadTimeDifference () { const serverTime = await this.fetchTime (); const after = this.milliseconds (); this.options['timeDifference'] = after - serverTime; return this.options['timeDifference']; } async fetchTime (params = {}) { const response = await this.publicGetTime (params); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: {}, // time_now: '1583933682.448826' // } // return this.safeTimestamp (response, 'time_now'); } async fetchMarkets (params = {}) { if (this.options['adjustForTimeDifference']) { await this.loadTimeDifference (); } const response = await this.publicGetSymbols (params); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // name: 'BTCUSD', // base_currency: 'BTC', // quote_currency: 'USD', // price_scale: 2, // taker_fee: '0.00075', // maker_fee: '-0.00025', // leverage_filter: { min_leverage: 1, max_leverage: 100, leverage_step: '0.01' }, // price_filter: { min_price: '0.5', max_price: '999999.5', tick_size: '0.5' }, // lot_size_filter: { max_trading_qty: 1000000, min_trading_qty: 1, qty_step: 1 } // }, // ], // time_now: '1583930495.454196' // } // const markets = this.safeValue (response, 'result', []); const options = this.safeValue (this.options, 'fetchMarkets', {}); const linearQuoteCurrencies = this.safeValue (options, 'linear', { 'USDT': true }); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'name'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const linear = (quote in linearQuoteCurrencies); const inverse = !linear; const symbol = base + '/' + quote; const lotSizeFilter = this.safeValue (market, 'lot_size_filter', {}); const priceFilter = this.safeValue (market, 'price_filter', {}); const precision = { 'amount': this.safeFloat (lotSizeFilter, 'qty_step'), 'price': this.safeFloat (priceFilter, 'tick_size'), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'active': undefined, 'precision': precision, 'taker': this.safeFloat (market, 'taker_fee'), 'maker': this.safeFloat (market, 'maker_fee'), 'type': 'future', 'spot': false, 'future': true, 'option': false, 'linear': linear, 'inverse': inverse, 'limits': { 'amount': { 'min': this.safeFloat (lotSizeFilter, 'min_trading_qty'), 'max': this.safeFloat (lotSizeFilter, 'max_trading_qty'), }, 'price': { 'min': this.safeFloat (priceFilter, 'min_price'), 'max': this.safeFloat (priceFilter, 'max_price'), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const defaultCode = this.safeValue (this.options, 'code', 'BTC'); const options = this.safeValue (this.options, 'fetchBalance', {}); const code = this.safeValue (options, 'code', defaultCode); const currency = this.currency (code); const request = { 'coin': currency['id'], }; const response = await this.privateGetWalletBalance (this.extend (request, params)); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: { // BTC: { // equity: 0, // available_balance: 0, // used_margin: 0, // order_margin: 0, // position_margin: 0, // occ_closing_fee: 0, // occ_funding_fee: 0, // wallet_balance: 0, // realised_pnl: 0, // unrealised_pnl: 0, // cum_realised_pnl: 0, // given_cash: 0, // service_cash: 0 // } // }, // time_now: '1583937810.370020', // rate_limit_status: 119, // rate_limit_reset_ms: 1583937810367, // rate_limit: 120 // } // const result = { 'info': response, }; const balances = this.safeValue (response, 'result', {}); const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const balance = balances[currencyId]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available_balance'); account['used'] = this.safeFloat (balance, 'used_margin'); account['total'] = this.safeFloat (balance, 'equity'); result[code] = account; } return this.parseBalance (result); } parseTicker (ticker, market = undefined) { // // fetchTicker // // { // symbol: 'BTCUSD', // bid_price: '7680', // ask_price: '7680.5', // last_price: '7680.00', // last_tick_direction: 'MinusTick', // prev_price_24h: '7870.50', // price_24h_pcnt: '-0.024204', // high_price_24h: '8035.00', // low_price_24h: '7671.00', // prev_price_1h: '7780.00', // price_1h_pcnt: '-0.012853', // mark_price: '7683.27', // index_price: '7682.74', // open_interest: 188829147, // open_value: '23670.06', // total_turnover: '25744224.90', // turnover_24h: '102997.83', // total_volume: 225448878806, // volume_24h: 809919408, // funding_rate: '0.0001', // predicted_funding_rate: '0.0001', // next_funding_time: '2020-03-12T00:00:00Z', // countdown_hour: 7 // } // const timestamp = undefined; const marketId = this.safeString (ticker, 'symbol'); let symbol = marketId; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last_price'); const open = this.safeFloat (ticker, 'prev_price_24h'); let percentage = this.safeFloat (ticker, 'price_24h_pcnt'); if (percentage !== undefined) { percentage *= 100; } let change = undefined; let average = undefined; if ((last !== undefined) && (open !== undefined)) { change = last - open; average = this.sum (open, last) / 2; } const baseVolume = this.safeFloat (ticker, 'turnover_24h'); const quoteVolume = this.safeFloat (ticker, 'volume_24h'); let vwap = undefined; if (quoteVolume !== undefined && baseVolume !== undefined) { vwap = quoteVolume / baseVolume; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high_price_24h'), 'low': this.safeFloat (ticker, 'low_price_24h'), 'bid': this.safeFloat (ticker, 'bid_price'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask_price'), 'askVolume': undefined, 'vwap': vwap, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTickers (this.extend (request, params)); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // symbol: 'BTCUSD', // bid_price: '7680', // ask_price: '7680.5', // last_price: '7680.00', // last_tick_direction: 'MinusTick', // prev_price_24h: '7870.50', // price_24h_pcnt: '-0.024204', // high_price_24h: '8035.00', // low_price_24h: '7671.00', // prev_price_1h: '7780.00', // price_1h_pcnt: '-0.012853', // mark_price: '7683.27', // index_price: '7682.74', // open_interest: 188829147, // open_value: '23670.06', // total_turnover: '25744224.90', // turnover_24h: '102997.83', // total_volume: 225448878806, // volume_24h: 809919408, // funding_rate: '0.0001', // predicted_funding_rate: '0.0001', // next_funding_time: '2020-03-12T00:00:00Z', // countdown_hour: 7 // } // ], // time_now: '1583948195.818255' // } // const result = this.safeValue (response, 'result', []); const first = this.safeValue (result, 0); const timestamp = this.safeTimestamp (response, 'time_now'); const ticker = this.parseTicker (first, market); ticker['timestamp'] = timestamp; ticker['datetime'] = this.iso8601 (timestamp); return ticker; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickers (params); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // symbol: 'BTCUSD', // bid_price: '7680', // ask_price: '7680.5', // last_price: '7680.00', // last_tick_direction: 'MinusTick', // prev_price_24h: '7870.50', // price_24h_pcnt: '-0.024204', // high_price_24h: '8035.00', // low_price_24h: '7671.00', // prev_price_1h: '7780.00', // price_1h_pcnt: '-0.012853', // mark_price: '7683.27', // index_price: '7682.74', // open_interest: 188829147, // open_value: '23670.06', // total_turnover: '25744224.90', // turnover_24h: '102997.83', // total_volume: 225448878806, // volume_24h: 809919408, // funding_rate: '0.0001', // predicted_funding_rate: '0.0001', // next_funding_time: '2020-03-12T00:00:00Z', // countdown_hour: 7 // } // ], // time_now: '1583948195.818255' // } // const result = this.safeValue (response, 'result', []); const tickers = {}; for (let i = 0; i < result.length; i++) { const ticker = this.parseTicker (result[i]); const symbol = ticker['symbol']; tickers[symbol] = ticker; } return this.filterByArray (tickers, 'symbol', symbols); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // { // symbol: 'BTCUSD', // interval: '1', // open_time: 1583952540, // open: '7760.5', // high: '7764', // low: '7757', // close: '7763.5', // volume: '1259766', // turnover: '162.32773718999994' // }, // // { // "id":143536, // "symbol":"BTCUSDT", // "period":"15", // "start_at":1587883500, // "volume":1.035, // "open":7540.5, // "high":7541, // "low":7540.5, // "close":7541 // } // return [ this.safeTimestamp2 (ohlcv, 'open_time', 'start_at'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat2 (ohlcv, 'turnover', 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'interval': this.timeframes[timeframe], }; const duration = this.parseTimeframe (timeframe); const now = this.seconds (); if (since === undefined) { if (limit === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument or a limit argument'); } else { request['from'] = now - limit * duration; } } else { request['from'] = parseInt (since / 1000); } if (limit !== undefined) { request['limit'] = limit; // max 200, default 200 } const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); const method = (marketType === 'linear') ? 'publicLinearGetKline' : 'publicGetKlineList'; const response = await this[method] (this.extend (request, params)); // // inverse perpetual BTC/USD // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // symbol: 'BTCUSD', // interval: '1', // open_time: 1583952540, // open: '7760.5', // high: '7764', // low: '7757', // close: '7763.5', // volume: '1259766', // turnover: '162.32773718999994' // }, // ], // time_now: '1583953082.397330' // } // // linear perpetual BTC/USDT // // { // "ret_code":0, // "ret_msg":"OK", // "ext_code":"", // "ext_info":"", // "result":[ // { // "id":143536, // "symbol":"BTCUSDT", // "period":"15", // "start_at":1587883500, // "volume":1.035, // "open":7540.5, // "high":7541, // "low":7540.5, // "close":7541 // } // ], // "time_now":"1587884120.168077" // } // const result = this.safeValue (response, 'result', {}); return this.parseOHLCVs (result, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // id: 43785688, // symbol: 'BTCUSD', // price: 7786, // qty: 67, // side: 'Sell', // time: '2020-03-11T19:18:30.123Z' // } // // fetchMyTrades, fetchOrderTrades (private) // // { // "closed_size": 0, // "cross_seq": 277136382, // "exec_fee": "0.0000001", // "exec_id": "256e5ef8-abfe-5772-971b-f944e15e0d68", // "exec_price": "8178.5", // "exec_qty": 1, // // the docs say the exec_time field is "abandoned" now // // the user should use "trade_time_ms" // "exec_time": "1571676941.70682", // "exec_type": "Trade", //Exec Type Enum // "exec_value": "0.00012227", // "fee_rate": "0.00075", // "last_liquidity_ind": "RemovedLiquidity", //Liquidity Enum // "leaves_qty": 0, // "nth_fill": 2, // "order_id": "7ad50cb1-9ad0-4f74-804b-d82a516e1029", // "order_link_id": "", // "order_price": "8178", // "order_qty": 1, // "order_type": "Market", //Order Type Enum // "side": "Buy", //Side Enum // "symbol": "BTCUSD", //Symbol Enum // "user_id": 1, // "trade_time_ms": 1577480599000 // } // const id = this.safeString2 (trade, 'id', 'exec_id'); let symbol = undefined; let base = undefined; const marketId = this.safeString (trade, 'symbol'); let amount = this.safeFloat2 (trade, 'qty', 'exec_qty'); let cost = this.safeFloat (trade, 'exec_value'); const price = this.safeFloat2 (trade, 'price', 'exec_price'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; base = market['base']; } if (market !== undefined) { if (symbol === undefined) { symbol = market['symbol']; base = market['base']; } // if private trade if ('exec_fee' in trade) { if (market['inverse']) { amount = this.safeFloat (trade, 'exec_value'); cost = this.safeFloat (trade, 'exec_qty'); } } } if (cost === undefined) { if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } } let timestamp = this.parse8601 (this.safeString (trade, 'time')); if (timestamp === undefined) { timestamp = this.safeInteger (trade, 'trade_time_ms'); } const side = this.safeStringLower (trade, 'side'); const lastLiquidityInd = this.safeString (trade, 'last_liquidity_ind'); const takerOrMaker = (lastLiquidityInd === 'AddedLiquidity') ? 'maker' : 'taker'; const feeCost = this.safeFloat (trade, 'exec_fee'); let fee = undefined; if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': base, 'rate': this.safeFloat (trade, 'fee_rate'), }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': this.safeString (trade, 'order_id'), 'type': this.safeStringLower (trade, 'order_type'), 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], // 'from': 123, // from id }; if (limit !== undefined) { request['count'] = limit; // default 500, max 1000 } const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); const method = (marketType === 'linear') ? 'publicLinearGetRecentTradingRecords' : 'publicGetTradingRecords'; const response = await this[method] (this.extend (request, params)); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { // id: 43785688, // symbol: 'BTCUSD', // price: 7786, // qty: 67, // side: 'Sell', // time: '2020-03-11T19:18:30.123Z' // }, // ], // time_now: '1583954313.393362' // } // const result = this.safeValue (response, 'result', {}); return this.parseTrades (result, market, since, limit); } parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'Buy', asksKey = 'Sell', priceKey = 'price', amountKey = 'size') { const bids = []; const asks = []; for (let i = 0; i < orderbook.length; i++) { const bidask = orderbook[i]; const side = this.safeString (bidask, 'side'); if (side === 'Buy') { bids.push (this.parseBidAsk (bidask, priceKey, amountKey)); } else if (side === 'Sell') { asks.push (this.parseBidAsk (bidask, priceKey, amountKey)); } else { throw new ExchangeError (this.id + ' parseOrderBook encountered an unrecognized bidask format: ' + this.json (bidask)); } } return { 'bids': this.sortBy (bids, 0, true), 'asks': this.sortBy (asks, 0), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'nonce': undefined, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetOrderBookL2 (this.extend (request, params)); // // { // ret_code: 0, // ret_msg: 'OK', // ext_code: '', // ext_info: '', // result: [ // { symbol: 'BTCUSD', price: '7767.5', size: 677956, side: 'Buy' }, // { symbol: 'BTCUSD', price: '7767', size: 580690, side: 'Buy' }, // { symbol: 'BTCUSD', price: '7766.5', size: 475252, side: 'Buy' }, // { symbol: 'BTCUSD', price: '7768', size: 330847, side: 'Sell' }, // { symbol: 'BTCUSD', price: '7768.5', size: 97159, side: 'Sell' }, // { symbol: 'BTCUSD', price: '7769', size: 6508, side: 'Sell' }, // ], // time_now: '1583954829.874823' // } // const result = this.safeValue (response, 'result', []); const timestamp = this.safeTimestamp (response, 'time_now'); return this.parseOrderBook (result, timestamp, 'Buy', 'Sell', 'price', 'size'); } parseOrderStatus (status) { const statuses = { // basic orders 'Created': 'open', 'Rejected': 'rejected', // order is triggered but failed upon being placed 'New': 'open', 'PartiallyFilled': 'open', 'Filled': 'filled', 'Cancelled': 'canceled', 'PendingCancel': 'canceling', // the engine has received the cancellation but there is no guarantee that it will be successful // conditional orders 'Active': 'closed', // order is triggered and placed successfully 'Untriggered': 'open', // order waits to be triggered 'Triggered': 'closed', // order is triggered // 'Cancelled': 'canceled', // order is cancelled // 'Rejected': 'rejected', // order is triggered but fail to be placed 'Deactivated': 'canceled', // conditional order was cancelled before triggering }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "user_id": 1, // "order_id": "335fd977-e5a5-4781-b6d0-c772d5bfb95b", // "symbol": "BTCUSD", // "side": "Buy", // "order_type": "Limit", // "price": 8800, // "qty": 1, // "time_in_force": "GoodTillCancel", // "order_status": "Created", // "last_exec_time": 0, // "last_exec_price": 0, // "leaves_qty": 1, // "cum_exec_qty": 0, // in contracts, where 1 contract = 1 quote currency unit (USD for inverse contracts) // "cum_exec_value": 0, // in contract's underlying currency (BTC for inverse contracts) // "cum_exec_fee": 0, // "reject_reason": "", // "order_link_id": "", // "created_at": "2019-11-30T11:03:43.452Z", // "updated_at": "2019-11-30T11:03:43.455Z" // } // // fetchOrder // // { // "user_id" : 599946, // "symbol" : "BTCUSD", // "side" : "Buy", // "order_type" : "Limit", // "price" : "7948", // "qty" : 10, // "time_in_force" : "GoodTillCancel", // "order_status" : "Filled", // "ext_fields" : { // "o_req_num" : -1600687220498, // "xreq_type" : "x_create" // }, // "last_exec_time" : "1588150113.968422", // "last_exec_price" : "7948", // "leaves_qty" : 0, // "leaves_value" : "0", // "cum_exec_qty" : 10, // "cum_exec_value" : "0.00125817", // "cum_exec_fee" : "-0.00000031", // "reject_reason" : "", // "cancel_type" : "", // "order_link_id" : "", // "created_at" : "2020-04-29T08:45:24.399146Z", // "updated_at" : "2020-04-29T08:48:33.968422Z", // "order_id" : "dd2504b9-0157-406a-99e1-efa522373944" // } // const marketId = this.safeString (order, 'symbol'); let symbol = undefined; let base = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const id = this.safeString (order, 'order_id'); const price = this.safeFloat (order, 'price'); const average = this.safeFloat (order, 'average_price'); let amount = undefined; let cost = undefined; let filled = undefined; let remaining = undefined; if (market !== undefined) { symbol = market['symbol']; base = market['base']; if (market['inverse']) { cost = this.safeFloat (order, 'cum_exec_qty'); filled = this.safeFloat (order, 'cum_exec_value'); remaining = this.safeFloat (order, 'leaves_value'); amount = this.sum (filled, remaining); } else { amount = this.safeFloat (order, 'qty'); cost = this.safeFloat (order, 'cum_exec_value'); filled = this.safeFloat (order, 'cum_exec_qty'); remaining = this.safeFloat (order, 'leaves_qty'); } } let lastTradeTimestamp = this.safeTimestamp (order, 'last_exec_time'); if (lastTradeTimestamp === 0) { lastTradeTimestamp = undefined; } if ((filled === undefined) && (amount !== undefined) && (remaining !== undefined)) { filled = amount - remaining; } if (filled !== undefined) { if ((remaining === undefined) && (amount !== undefined)) { remaining = amount - filled; } if (cost === undefined) { if (price !== undefined) { cost = price * filled; } } } const status = this.parseOrderStatus (this.safeString (order, 'order_status')); const side = this.safeStringLower (order, 'side'); let feeCost = this.safeFloat (order, 'cum_exec_fee'); let fee = undefined; if (feeCost !== undefined) { feeCost = Math.abs (feeCost); fee = { 'cost': feeCost, 'currency': base, }; } const type = this.safeStringLower (order, 'order_type'); let clientOrderId = this.safeString (order, 'order_link_id'); if ((clientOrderId !== undefined) && (clientOrderId.length < 1)) { clientOrderId = undefined; } return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], // 'order_link_id': 'string', // one of order_id, stop_order_id or order_link_id is required // regular orders --------------------------------------------- // 'order_id': id, // one of order_id or order_link_id is required for regular orders // conditional orders --------------------------------------------- // 'stop_order_id': id, // one of stop_order_id or order_link_id is required for conditional orders }; const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); let method = (marketType === 'linear') ? 'privateLinearGetOrderSearch' : 'privateGetOrder'; const stopOrderId = this.safeString (params, 'stop_order_id'); if (stopOrderId === undefined) { const orderLinkId = this.safeString (params, 'order_link_id'); if (orderLinkId === undefined) { request['order_id'] = id; } } else { method = (marketType === 'linear') ? 'privateLinearGetStopOrderSearch' : 'privateGetStopOrder'; } const response = await this[method] (this.extend (request, params)); // // { // "ret_code": 0, // "ret_msg": "OK", // "ext_code": "", // "ext_info": "", // "result": { // "user_id": 1, // "symbol": "BTCUSD", // "side": "Sell", // "order_type": "Limit", // "price": "8083", // "qty": 10, // "time_in_force": "GoodTillCancel", // "order_status": "New", // "ext_fields": { "o_req_num": -308787, "xreq_type": "x_create", "xreq_offset": 4154640 }, // "leaves_qty": 10, // "leaves_value": "0.00123716", // "cum_exec_qty": 0, // "reject_reason": "", // "order_link_id": "", // "created_at": "2019-10-21T07:28:19.396246Z", // "updated_at": "2019-10-21T07:28:19.396246Z", // "order_id": "efa44157-c355-4a98-b6d6-1d846a936b93" // }, // "time_now": "1571651135.291930", // "rate_limit_status": 99, // The remaining number of accesses in one minute // "rate_limit_reset_ms": 1580885703683, // "rate_limit": 100 // } // // conditional orders // // { // "ret_code": 0, // "ret_msg": "OK", // "ext_code": "", // "ext_info": "", // "result": { // "user_id": 1, // "symbol": "BTCUSD", // "side": "Buy", // "order_type": "Limit", // "price": "8000", // "qty": 1, // "time_in_force": "GoodTillCancel", // "order_status": "Untriggered", // "ext_fields": {}, // "leaves_qty": 1, // "leaves_value": "0.00013333", // "cum_exec_qty": 0, // "cum_exec_value": null, // "cum_exec_fee": null, // "reject_reason": "", // "order_link_id": "", // "created_at": "2019-12-27T19:56:24.052194Z", // "updated_at": "2019-12-27T19:56:24.052194Z", // "order_id": "378a1bbc-a93a-4e75-87f4-502ea754ba36" // }, // "time_now": "1577476584.386958", // "rate_limit_status": 99, // "rate_limit_reset_ms": 1580885703683, // "rate_limit": 100 // } // const result = this.safeValue (response, 'result'); return this.parseOrder (result, market); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { // orders --------------------------------------------------------- 'side': this.capitalize (side), 'symbol': market['id'], 'order_type': this.capitalize (type), 'qty': this.amountToPrecision (symbol, amount), // order quantity in USD, integer only // 'price': this.priceToPrecision (symbol, price), // required for limit orders 'time_in_force': 'GoodTillCancel', // ImmediateOrCancel, FillOrKill, PostOnly // 'take_profit': 123.45, // take profit price, only take effect upon opening the position // 'stop_loss': 123.45, // stop loss price, only take effect upon opening the position // 'reduce_only': false, // reduce only // when creating a closing order, bybit recommends a True value for // close_on_trigger to avoid failing due to insufficient available margin // 'close_on_trigger': false, // 'order_link_id': 'string', // unique client order id, max 36 characters // conditional orders --------------------------------------------- // base_price is used to compare with the value of stop_px, to decide // whether your conditional order will be triggered by crossing trigger // price from upper side or lower side, mainly used to identify the // expected direction of the current conditional order // 'base_price': 123.45, // required for conditional orders // 'stop_px': 123.45, // trigger price, required for conditional orders // 'trigger_by': 'LastPrice', // IndexPrice, MarkPrice }; let priceIsRequired = false; if (type === 'limit') { priceIsRequired = true; } if (priceIsRequired) { if (price !== undefined) { request['price'] = this.priceToPrecision (symbol, price); } else { throw new ArgumentsRequired (this.id + ' createOrder requires a price argument for a ' + type + ' order'); } } const stopPx = this.safeValue (params, 'stop_px'); const basePrice = this.safeValue (params, 'base_price'); const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); let method = (marketType === 'linear') ? 'privateLinearPostOrderCreate' : 'privatePostOrderCreate'; if (stopPx !== undefined) { if (basePrice === undefined) { throw new ArgumentsRequired (this.id + ' createOrder requires both the stop_px and base_price params for a conditional ' + type + ' order'); } else { method = (marketType === 'linear') ? 'privateLinearPostStopOrderCreate' : 'openapiPostStopOrderCreate'; request['stop_px'] = this.priceToPrecision (symbol, stopPx); request['base_price'] = this.priceToPrecision (symbol, basePrice); params = this.omit (params, [ 'stop_px', 'base_price' ]); } } else if (basePrice !== undefined) { throw new ArgumentsRequired (this.id + ' createOrder requires both the stop_px and base_price params for a conditional ' + type + ' order'); } const response = await this[method] (this.extend (request, params)); // // { // "ret_code": 0, // "ret_msg": "OK", // "ext_code": "", // "ext_info": "", // "result": { // "user_id": 1, // "order_id": "335fd977-e5a5-4781-b6d0-c772d5bfb95b", // "symbol": "BTCUSD", // "side": "Buy", // "order_type": "Limit", // "price": 8800, // "qty": 1, // "time_in_force": "GoodTillCancel", // "order_status": "Created", // "last_exec_time": 0, // "last_exec_price": 0, // "leaves_qty": 1, // "cum_exec_qty": 0, // "cum_exec_value": 0, // "cum_exec_fee": 0, // "reject_reason": "", // "order_link_id": "", // "created_at": "2019-11-30T11:03:43.452Z", // "updated_at": "2019-11-30T11:03:43.455Z" // }, // "time_now": "1575111823.458705", // "rate_limit_status": 98, // "rate_limit_reset_ms": 1580885703683, // "rate_limit": 100 // } // // conditional orders // // { // "ret_code": 0, // "ret_msg": "ok", // "ext_code": "", // "result": { // "user_id": 1, // "symbol": "BTCUSD", // "side": "Buy", // "order_type": "Limit", // "price": 8000, // "qty": 1, // "time_in_force": "GoodTillCancel", // "stop_order_type": "Stop", // "trigger_by": "LastPrice", // "base_price": 7000, // "order_status": "Untriggered", // "ext_fields": { // "stop_order_type": "Stop", // "trigger_by": "LastPrice", // "base_price": 7000, // "expected_direction": "Rising", // "trigger_price": 7500, // "op_from": "api", // "remark": "127.0.01", // "o_req_num": 0 // }, // "leaves_qty": 1, // "leaves_value": 0.00013333, // "reject_reason": null, // "cross_seq": -1, // "created_at": "2019-12-27T12:48:24.000Z", // "updated_at": "2019-12-27T12:48:24.000Z", // "stop_px": 7500, // "stop_order_id": "a85cd1c0-a9a4-49d3-a1bd-bab5ebe946d5" // }, // "ext_info": null, // "time_now": "1577450904.327654", // "rate_limit_status": 99, // "rate_limit_reset_ms": 1577450904335, // "rate_limit": "100" // } // const result = this.safeValue (response, 'result'); return this.parseOrder (result, market); } async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' editOrder requires an symbol argument'); } const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); if (marketType === 'linear') { throw new NotSupported (this.id + ' does not support editOrder for ' + marketType + ' ' + symbol + ' market type'); } await this.loadMarkets (); const market = this.market (symbol); const request = { // 'order_id': id, // only for non-conditional orders 'symbol': market['id'], // 'p_r_qty': this.amountToPrecision (symbol, amount), // new order quantity, optional // 'p_r_price' this.priceToprecision (symbol, price), // new order price, optional // ---------------------------------------------------------------- // conditional orders // 'stop_order_id': id, // only for conditional orders // 'p_r_trigger_price': 123.45, // new trigger price also known as stop_px }; const stopOrderId = this.safeString (params, 'stop_order_id'); let method = 'openapiPostOrderReplace'; if (stopOrderId !== undefined) { method = 'openapiPostStopOrderReplace'; request['stop_order_id'] = stopOrderId; params = this.omit (params, [ 'stop_order_id' ]); } else { request['order_id'] = id; } if (amount !== undefined) { request['p_r_qty'] = this.amountToPrecision (symbol, amount); } if (price !== undefined) { request['p_r_price'] = this.priceToPrecision (symbol, price); } const response = await this[method] (this.extend (request, params)); // // { // "ret_code": 0, // "ret_msg": "ok", // "ext_code": "", // "result": { "order_id": "efa44157-c355-4a98-b6d6-1d846a936b93" }, // "time_now": "1539778407.210858", // "rate_limit_status": 99, // remaining number of accesses in one minute // "rate_limit_reset_ms": 1580885703683, // "rate_limit": 100 // } // // conditional orders // // { // "ret_code": 0, // "ret_msg": "ok", // "ext_code": "", // "result": { "stop_order_id": "378a1bbc-a93a-4e75-87f4-502ea754ba36" }, // "ext_info": null, // "time_now": "1577475760.604942", // "rate_limit_status": 96, // "rate_limit_reset_ms": 1577475760612, // "rate_limit": "100" // } // const result = this.safeValue (response, 'result', {}); return { 'info': response, 'id': this.safeString2 (result, 'order_id', 'stop_order_id'), 'order_id': this.safeString (result, 'order_id'), 'stop_order_id': this.safeString (result, 'stop_order_id'), }; } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], // 'order_link_id': 'string', // one of order_id, stop_order_id or order_link_id is required // regular orders --------------------------------------------- // 'order_id': id, // one of order_id or order_link_id is required for regular orders // conditional orders --------------------------------------------- // 'stop_order_id': id, // one of stop_order_id or order_link_id is required for conditional orders }; const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeValue (marketTypes, symbol); let method = (marketType === 'linear') ? 'privateLinearPostOrderCancel' : 'privatePostOrderCancel'; const stopOrderId = this.safeString (params, 'stop_order_id'); if (stopOrderId === undefined) { const orderLinkId = this.safeString (params, 'order_link_id'); if (orderLinkId === undefined) { request['order_id'] = id; } } else { method = (marketType === 'linear') ? 'privateLinearPostStopOrderCancel' : 'openapiPostStopOrderCancel'; } const response = await this[method] (this.extend (request, params)); const result = this.safeValue (response, 'result', {}); return this.parseOrder (result, market); } async cancelAllOrders (symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const options = this.safeValue (this.options, 'cancelAllOrders'); const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); const defaultMethod = (marketType === 'linear') ? 'privateLinearPostOrderCancelAll' : 'privatePostOrderCancelAll'; const method = this.safeString (options, 'method', defaultMethod); const response = await this[method] (this.extend (request, params)); const result = this.safeValue (response, 'result', []); return this.parseOrders (result, market); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'order_id': 'string' // 'order_link_id': 'string', // unique client order id, max 36 characters // 'symbol': market['id'], // default BTCUSD // 'order': 'desc', // asc // 'page': 1, // 'limit': 20, // max 50 // 'order_status': 'Created,New' // conditional orders --------------------------------------------- // 'stop_order_id': 'string', // 'stop_order_status': 'Untriggered', }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const options = this.safeValue (this.options, 'fetchOrders', {}); const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); let defaultMethod = (marketType === 'linear') ? 'privateLinearGetOrderList' : 'openapiGetOrderList'; let query = params; if (('stop_order_id' in params) || ('stop_order_status' in params)) { let stopOrderStatus = this.safeValue (params, 'stopOrderStatus'); if (stopOrderStatus !== undefined) { if (Array.isArray (stopOrderStatus)) { stopOrderStatus = stopOrderStatus.join (','); } request['stop_order_status'] = stopOrderStatus; query = this.omit (params, 'stop_order_status'); } defaultMethod = (marketType === 'linear') ? 'privateLinearGetStopOrderList' : 'openapiGetStopOrderList'; } const method = this.safeString (options, 'method', defaultMethod); const response = await this[method] (this.extend (request, query)); // // { // "ret_code": 0, // "ret_msg": "ok", // "ext_code": "", // "result": { // "current_page": 1, // "last_page": 6, // "data": [ // { // "user_id": 1, // "symbol": "BTCUSD", // "side": "Sell", // "order_type": "Market", // "price": 7074, // "qty": 2, // "time_in_force": "ImmediateOrCancel", // "order_status": "Filled", // "ext_fields": { // "close_on_trigger": true, // "orig_order_type": "BLimit", // "prior_x_req_price": 5898.5, // "op_from": "pc", // "remark": "127.0.0.1", // "o_req_num": -34799032763, // "xreq_type": "x_create" // }, // "last_exec_time": "1577448481.696421", // "last_exec_price": 7070.5, // "leaves_qty": 0, // "leaves_value": 0, // "cum_exec_qty": 2, // "cum_exec_value": 0.00028283, // "cum_exec_fee": 0.00002, // "reject_reason": "NoError", // "order_link_id": "", // "created_at": "2019-12-27T12:08:01.000Z", // "updated_at": "2019-12-27T12:08:01.000Z", // "order_id": "f185806b-b801-40ff-adec-52289370ed62" // } // ] // }, // "ext_info": null, // "time_now": "1577448922.437871", // "rate_limit_status": 98, // "rate_limit_reset_ms": 1580885703683, // "rate_limit": 100 // } // // conditional orders // // { // "ret_code": 0, // "ret_msg": "ok", // "ext_code": "", // "result": { // "current_page": 1, // "last_page": 1, // "data": [ // { // "user_id": 1, // "stop_order_status": "Untriggered", // "symbol": "BTCUSD", // "side": "Buy", // "order_type": "Limit", // "price": 8000, // "qty": 1, // "time_in_force": "GoodTillCancel", // "stop_order_type": "Stop", // "trigger_by": "LastPrice", // "base_price": 7000, // "order_link_id": "", // "created_at": "2019-12-27T12:48:24.000Z", // "updated_at": "2019-12-27T12:48:24.000Z", // "stop_px": 7500, // "stop_order_id": "a85cd1c0-a9a4-49d3-a1bd-bab5ebe946d5" // }, // ] // }, // "ext_info": null, // "time_now": "1577451658.755468", // "rate_limit_status": 599, // "rate_limit_reset_ms": 1577451658762, // "rate_limit": 600 // } // const result = this.safeValue (response, 'result', {}); const data = this.safeValue (result, 'data', []); return this.parseOrders (data, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const defaultStatuses = [ 'Rejected', 'Filled', 'Cancelled', // conditional orders // 'Active', // 'Triggered', // 'Cancelled', // 'Rejected', // 'Deactivated', ]; const options = this.safeValue (this.options, 'fetchClosedOrders', {}); let status = this.safeValue (options, 'order_status', defaultStatuses); if (Array.isArray (status)) { status = status.join (','); } const request = {}; const stopOrderStatus = this.safeValue (params, 'stop_order_status'); if (stopOrderStatus === undefined) { request['order_status'] = status; } else { request['stop_order_status'] = stopOrderStatus; } return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const defaultStatuses = [ 'Created', 'New', 'PartiallyFilled', 'PendingCancel', // conditional orders // 'Untriggered', ]; const options = this.safeValue (this.options, 'fetchOpenOrders', {}); let status = this.safeValue (options, 'order_status', defaultStatuses); if (Array.isArray (status)) { status = status.join (','); } const request = {}; const stopOrderStatus = this.safeValue (params, 'stop_order_status'); if (stopOrderStatus === undefined) { request['order_status'] = status; } else { request['stop_order_status'] = stopOrderStatus; } return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'order_id': id, }; return await this.fetchMyTrades (symbol, since, limit, this.extend (request, params)); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'order_id': 'f185806b-b801-40ff-adec-52289370ed62', // if not provided will return user's trading records // 'symbol': market['id'], // 'start_time': parseInt (since / 1000), // 'page': 1, // 'limit' 20, // max 50 }; let market = undefined; if (symbol === undefined) { const orderId = this.safeString (params, 'order_id'); if (orderId === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument or an order_id param'); } else { request['order_id'] = orderId; params = this.omit (params, 'order_id'); } } else { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['start_time'] = since; } if (limit !== undefined) { request['limit'] = limit; // default 20, max 50 } const marketTypes = this.safeValue (this.options, 'marketTypes', {}); const marketType = this.safeString (marketTypes, symbol); const method = (marketType === 'linear') ? 'privateLinearGetTradeExecutionList' : 'privateGetExecutionList'; const response = await this[method] (this.extend (request, params)); // // { // "ret_code": 0, // "ret_msg": "OK", // "ext_code": "", // "ext_info": "", // "result": { // "order_id": "Abandoned!!", // Abandoned!! // "trade_list": [ // { // "closed_size": 0, // "cross_seq": 277136382, // "exec_fee": "0.0000001", // "exec_id": "256e5ef8-abfe-5772-971b-f944e15e0d68", // "exec_price": "8178.5", // "exec_qty": 1, // "exec_time": "1571676941.70682", // "exec_type": "Trade", //Exec Type Enum // "exec_value": "0.00012227", // "fee_rate": "0.00075", // "last_liquidity_ind": "RemovedLiquidity", //Liquidity Enum // "leaves_qty": 0, // "nth_fill": 2, // "order_id": "7ad50cb1-9ad0-4f74-804b-d82a516e1029", // "order_link_id": "", // "order_price": "8178", // "order_qty": 1, // "order_type": "Market", //Order Type Enum // "side": "Buy", //Side Enum // "symbol": "BTCUSD", //Symbol Enum // "user_id": 1 // } // ] // }, // "time_now": "1577483699.281488", // "rate_limit_status": 118, // "rate_limit_reset_ms": 1577483699244737, // "rate_limit": 120 // } // const result = this.safeValue (response, 'result', {}); const trades = this.safeValue (result, 'trade_list', []); return this.parseTrades (trades, market, since, limit); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; if (limit !== undefined) { request['count'] = limit; } const response = await this.privateGetGetDeposits (this.extend (request, params)); // // { // "jsonrpc": "2.0", // "id": 5611, // "result": { // "count": 1, // "data": [ // { // "address": "2N35qDKDY22zmJq9eSyiAerMD4enJ1xx6ax", // "amount": 5, // "currency": "BTC", // "received_timestamp": 1549295017670, // "state": "completed", // "transaction_id": "230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda", // "updated_timestamp": 1549295130159 // } // ] // } // } // const result = this.safeValue (response, 'result', {}); const data = this.safeValue (result, 'data', []); return this.parseTransactions (data, currency, since, limit, params); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'coin': currency['id'], // 'start_date': this.iso8601 (since), // 'end_date': this.iso8601 (till), // 'status': 'Pending', // ToBeConfirmed, UnderReview, Pending, Success, CancelByUser, Reject, Expire // 'page': 1, // 'limit': 20, // max 50 }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['coin'] = currency['id']; } if (since !== undefined) { request['start_date'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.openapiGetWalletWithdrawList (this.extend (request, params)); // // { // "ret_code": 0, // "ret_msg": "ok", // "ext_code": "", // "result": { // "data": [ // { // "id": 137, // "user_id": 1, // "coin": "XRP", // Coin Enum // "status": "Pending", // Withdraw Status Enum // "amount": "20.00000000", // "fee": "0.25000000", // "address": "rH7H595XYEVTEHU2FySYsWnmfACBnZS9zM", // "tx_id": "", // "submited_at": "2019-06-11T02:20:24.000Z", // "updated_at": "2019-06-11T02:20:24.000Z" // }, // ], // "current_page": 1, // "last_page": 1 // }, // "ext_info": null, // "time_now": "1577482295.125488", // "rate_limit_status": 119, // "rate_limit_reset_ms": 1577482295132, // "rate_limit": 120 // } // const result = this.safeValue (response, 'result', {}); const data = this.safeValue (result, 'data', []); return this.parseTransactions (data, currency, since, limit, params); } parseTransactionStatus (status) { const statuses = { 'ToBeConfirmed': 'pending', 'UnderReview': 'pending', 'Pending': 'pending', 'Success': 'ok', 'CancelByUser': 'canceled', 'Reject': 'rejected', 'Expire': 'expired', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchWithdrawals // // { // "id": 137, // "user_id": 1, // "coin": "XRP", // Coin Enum // "status": "Pending", // Withdraw Status Enum // "amount": "20.00000000", // "fee": "0.25000000", // "address": "rH7H595XYEVTEHU2FySYsWnmfACBnZS9zM", // "tx_id": "", // "submited_at": "2019-06-11T02:20:24.000Z", // "updated_at": "2019-06-11T02:20:24.000Z" // } // const currencyId = this.safeString (transaction, 'coin'); const code = this.safeCurrencyCode (currencyId, currency); const timestamp = this.parse8601 (this.safeString (transaction, 'submited_at')); const updated = this.parse8601 (this.safeString (transaction, 'updated_at')); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const address = this.safeString (transaction, 'address'); const feeCost = this.safeFloat (transaction, 'fee'); let fee = undefined; if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': code, }; } return { 'info': transaction, 'id': this.safeString (transaction, 'id'), 'txid': this.safeString (transaction, 'tx_id'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'addressTo': undefined, 'addressFrom': undefined, 'tag': undefined, 'tagTo': undefined, 'tagFrom': undefined, 'type': 'withdrawal', 'amount': this.safeFloat (transaction, 'amount'), 'currency': code, 'status': status, 'updated': updated, 'fee': fee, }; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'coin': currency['id'], // 'currency': currency['id'], // alias // 'start_date': this.iso8601 (since), // 'end_date': this.iso8601 (till), // 'wallet_fund_type': 'Deposit', // Withdraw, RealisedPNL, Commission, Refund, Prize, ExchangeOrderWithdraw, ExchangeOrderDeposit // 'page': 1, // 'limit': 20, // max 50 }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['coin'] = currency['id']; } if (since !== undefined) { request['start_date'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.openapiGetWalletFundRecords (this.extend (request, params)); // // { // "ret_code": 0, // "ret_msg": "ok", // "ext_code": "", // "result": { // "data": [ // { // "id": 234467, // "user_id": 1, // "coin": "BTC", // "wallet_id": 27913, // "type": "Realized P&L", // "amount": "-0.00000006", // "tx_id": "", // "address": "BTCUSD", // "wallet_balance": "0.03000330", // "exec_time": "2019-12-09T00:00:25.000Z", // "cross_seq": 0 // } // ] // }, // "ext_info": null, // "time_now": "1577481867.115552", // "rate_limit_status": 119, // "rate_limit_reset_ms": 1577481867122, // "rate_limit": 120 // } // const result = this.safeValue (response, 'result', {}); const data = this.safeValue (result, 'data', []); return this.parseLedger (data, currency, since, limit); } parseLedgerEntry (item, currency = undefined) { // // { // "id": 234467, // "user_id": 1, // "coin": "BTC", // "wallet_id": 27913, // "type": "Realized P&L", // "amount": "-0.00000006", // "tx_id": "", // "address": "BTCUSD", // "wallet_balance": "0.03000330", // "exec_time": "2019-12-09T00:00:25.000Z", // "cross_seq": 0 // } // const currencyId = this.safeString (item, 'coin'); const code = this.safeCurrencyCode (currencyId, currency); const amount = this.safeFloat (item, 'amount'); const after = this.safeFloat (item, 'wallet_balance'); const direction = (amount < 0) ? 'out' : 'in'; let before = undefined; if (after !== undefined && amount !== undefined) { const difference = (direction === 'out') ? amount : -amount; before = this.sum (after, difference); } const timestamp = this.parse8601 (this.safeString (item, 'exec_time')); const type = this.parseLedgerEntryType (this.safeString (item, 'type')); const id = this.safeString (item, 'id'); const referenceId = this.safeString (item, 'tx_id'); return { 'id': id, 'currency': code, 'account': this.safeString (item, 'wallet_id'), 'referenceAccount': undefined, 'referenceId': referenceId, 'status': undefined, 'amount': amount, 'before': before, 'after': after, 'fee': undefined, 'direction': direction, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'type': type, 'info': item, }; } parseLedgerEntryType (type) { const types = { 'Deposit': 'transaction', 'Withdraw': 'transaction', 'RealisedPNL': 'trade', 'Commission': 'fee', 'Refund': 'cashback', 'Prize': 'prize', // ? 'ExchangeOrderWithdraw': 'transaction', 'ExchangeOrderDeposit': 'transaction', }; return this.safeString (types, type, type); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api']; let request = path; // public v2 if (api === 'public') { request = '/' + this.version + '/' + api + '/' + request; if (Object.keys (params).length) { request += '?' + this.rawencode (params); } } else if (api === 'publicLinear') { request = '/public/linear/' + request; if (Object.keys (params).length) { request += '?' + this.rawencode (params); } } else { this.checkRequiredCredentials (); if (api === 'openapi') { request = '/open-api/' + request; } else if (api === 'private') { // private v2 request = '/' + this.version + '/' + api + '/' + request; } else if (api === 'privateLinear') { request = '/private/linear/' + request; } else { // position, user request = '/' + api + '/' + request; } const timestamp = this.nonce (); const query = this.extend (params, { 'api_key': this.apiKey, 'recv_window': this.options['recvWindow'], 'timestamp': timestamp, }); const auth = this.rawencode (this.keysort (query)); const signature = this.hmac (this.encode (auth), this.encode (this.secret)); if (method === 'POST') { body = this.json (this.extend (query, { 'sign': signature, })); headers = { 'Content-Type': 'application/json', }; } else { request += '?' + auth + '&sign=' + signature; } } url += request; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (!response) { return; // fallback to default error handler } // // { // ret_code: 10001, // ret_msg: 'ReadMapCB: expect { or n, but found \u0000, error ' + // 'found in #0 byte of ...||..., bigger context ' + // '...||...', // ext_code: '', // ext_info: '', // result: null, // time_now: '1583934106.590436' // } // const errorCode = this.safeValue (response, 'ret_code'); if (errorCode !== 0) { const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],57:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, BadRequest, AuthenticationError, DDoSProtection, BadResponse } = require ('./base/errors'); const { TRUNCATE, NO_PADDING, DECIMAL_PLACES } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class bytetrade extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bytetrade', 'name': 'ByteTrade', 'countries': ['HK'], 'rateLimit': 500, 'requiresWeb3': true, 'certified': true, // new metainfo interface 'has': { 'fetchCurrencies': true, 'fetchDepositAddress': true, 'CORS': false, 'fetchBidsAsks': true, 'fetchTickers': true, 'fetchOHLCV': true, 'fetchMyTrades': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'withdraw': true, 'fetchDeposits': true, 'fetchWithdrawals': true, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '4h': '4h', '1d': '1d', '5d': '5d', '1w': '1w', '1M': '1M', }, 'urls': { 'test': { 'market': 'https://api-v2-test.byte-trade.com', 'public': 'https://api-v2-test.byte-trade.com', }, 'logo': 'https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg', 'api': { 'market': 'https://api-v2.bytetrade.com', 'public': 'https://api-v2.bytetrade.com', }, 'www': 'https://www.byte-trade.com', 'doc': 'https://github.com/Bytetrade/bytetrade-official-api-docs/wiki', }, 'api': { 'market': { 'get': [ 'klines', // Kline of a symbol 'depth', // Market Depth of a symbol 'trades', // Trade records of a symbol 'tickers', ], }, 'public': { 'get': [ 'symbols', // Reference information of trading instrument, including base currency, quote precision, etc. 'currencies', // The list of currencies available 'balance', // Get the balance of an account 'orders/open', // Get the open orders of an account 'orders/closed', // Get the closed orders of an account 'orders/all', // Get the open and closed orders of an account 'orders', // Get the details of an order of an account 'orders/trades', // Get detail match results 'depositaddress', // Get deposit address 'withdrawals', // Get withdrawals info 'deposits', // Get deposit info 'transfers', // Get transfer info ], 'post': [ 'transaction/createorder', // Post create order transaction to blockchain 'transaction/cancelorder', // Post cancel order transaction to blockchain 'transaction/withdraw', // Post withdraw transaction to blockchain 'transaction/transfer', // Post transfer transaction to blockchain ], }, }, 'fees': { 'trading': { 'taker': 0.0008, 'maker': 0.0008, }, }, 'commonCurrencies': { '44': 'ByteHub', '48': 'Blocktonic', }, 'exceptions': { 'vertify error': AuthenticationError, // typo on the exchange side, 'vertify' 'verify error': AuthenticationError, // private key signature is incorrect 'transaction already in network': BadRequest, // same transaction submited 'invalid argument': BadRequest, }, }); } async fetchCurrencies (params = {}) { const currencies = await this.publicGetCurrencies (params); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'code'); let code = undefined; if (id in this.commonCurrencies) { code = this.commonCurrencies[id]; } else { code = this.safeString (currency, 'name'); } const name = this.safeString (currency, 'fullname'); // in byte-trade.com DEX, request https://api-v2.byte-trade.com/currencies will return currencies, // the api doc is https://github.com/Bytetrade/bytetrade-official-api-docs/wiki/rest-api#get-currencies-get-currencys-supported-in-bytetradecom // we can see the coin name is none-unique in the result, the coin which code is 18 is the CyberMiles ERC20, and the coin which code is 35 is the CyberMiles main chain, but their name is same. // that is because bytetrade is a DEX, supports people create coin with the same name, but the id(code) of coin is unique, so we should use the id or name and id as the identity of coin. // For coin name and symbol is same with CCXT, I use name@id as the key of commonCurrencies dict. // [{ // "name": "CMT", // currency name, non-unique // "code": "18", // currency id, unique // "type": "crypto", // "fullname": "CyberMiles", // "active": true, // "chainType": "ethereum", // "basePrecision": 18, // "transferPrecision": 10, // "externalPrecision": 18, // "chainContractAddress": "0xf85feea2fdd81d51177f6b8f35f0e6734ce45f5f", // "limits": { // "deposit": { // "min": "0", // "max": "-1" // }, // "withdraw": { // "min": "0", // "max": "-1" // } // } // }, // { // "name": "CMT", // "code": "35", // "type": "crypto", // "fullname": "CyberMiles", // "active": true, // "chainType": "cmt", // "basePrecision": 18, // "transferPrecision": 10, // "externalPrecision": 18, // "chainContractAddress": "0x0000000000000000000000000000000000000000", // "limits": { // "deposit": { // "min": "1", // "max": "-1" // }, // "withdraw": { // "min": "10", // "max": "-1" // } // } // } // ] const active = this.safeValue (currency, 'active'); const limits = this.safeValue (currency, 'limits'); const deposit = this.safeValue (limits, 'deposit'); const amountPrecision = this.safeInteger (currency, 'basePrecision'); let maxDeposit = this.safeFloat (deposit, 'max'); if (maxDeposit === -1.0) { maxDeposit = undefined; } const withdraw = this.safeValue (limits, 'withdraw'); let maxWithdraw = this.safeFloat (withdraw, 'max'); if (maxWithdraw === -1.0) { maxWithdraw = undefined; } result[code] = { 'id': id, 'code': code, 'name': name, 'active': active, 'precision': { 'amount': amountPrecision, 'price': undefined, }, 'fee': undefined, 'limits': { 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, 'deposit': { 'min': this.safeFloat (deposit, 'min'), 'max': maxDeposit, }, 'withdraw': { 'min': this.safeFloat (withdraw, 'min'), 'max': maxWithdraw, }, }, 'info': currency, }; } return result; } async fetchMarkets (params = {}) { const markets = await this.publicGetSymbols (params); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'symbol'); let base = this.safeString (market, 'baseName'); let quote = this.safeString (market, 'quoteName'); const normalBase = base.split ('@')[0]; const normalQuote = quote.split ('@')[0]; const normalSymbol = normalBase + '/' + normalQuote; const baseId = this.safeString (market, 'base'); const quoteId = this.safeString (market, 'quote'); if (baseId in this.commonCurrencies) { base = this.commonCurrencies[baseId]; } if (quoteId in this.commonCurrencies) { quote = this.commonCurrencies[quoteId]; } const symbol = base + '/' + quote; const limits = this.safeValue (market, 'limits', {}); const amount = this.safeValue (limits, 'amount', {}); const price = this.safeValue (limits, 'price', {}); const precision = this.safeValue (market, 'precision', {}); const active = this.safeString (market, 'active'); const entry = { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': active, 'precision': { 'amount': this.safeInteger (precision, 'amount'), 'price': this.safeInteger (precision, 'price'), }, 'normalSymbol': normalSymbol, 'limits': { 'amount': { 'min': this.safeFloat (amount, 'min'), 'max': this.safeFloat (amount, 'max'), }, 'price': { 'min': this.safeFloat (price, 'min'), 'max': this.safeFloat (price, 'max'), }, 'cost': { 'min': undefined, 'max': undefined, }, }, }; result.push (entry); } return result; } async fetchBalance (params = {}) { if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired (this.id + ' fetchDeposits requires this.apiKey or userid argument'); } await this.loadMarkets (); const request = { 'userid': this.apiKey, }; const balances = await this.publicGetBalance (this.extend (request, params)); const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'code'); const code = this.safeCurrencyCode (currencyId, undefined); const account = this.account (); account['free'] = this.safeFloat (balance, 'free'); account['used'] = this.safeFloat (balance, 'used'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = maximum = 100 } const response = await this.marketGetDepth (this.extend (request, params)); const timestamp = this.safeValue (response, 'timestamp'); const orderbook = this.parseOrderBook (response, timestamp); return orderbook; } parseTicker (ticker, market = undefined) { const timestamp = this.safeInteger (ticker, 'timestamp'); // // [ // { // "symbol":"68719476706", // "name":"ETH/BTC", // "base":"2", // "quote":"32", // "timestamp":1575905991933, // "datetime":"2019-12-09T15:39:51.933Z", // "high":"0", // "low":"0", // "open":"0", // "close":"0", // "last":"0", // "change":"0", // "percentage":"0", // "baseVolume":"0", // "quoteVolume":"0" // } // ] // let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const baseId = this.safeString (ticker, 'base'); const quoteId = this.safeString (ticker, 'quote'); if ((baseId !== undefined) && (quoteId !== undefined)) { const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': this.safeFloat (ticker, 'weightedAvgPrice'), 'open': this.safeFloat (ticker, 'open'), 'close': this.safeFloat (ticker, 'close'), 'last': this.safeFloat (ticker, 'last'), 'previousClose': undefined, // previous day close 'change': this.safeFloat (ticker, 'change'), 'percentage': this.safeFloat (ticker, 'percentage'), 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'baseVolume'), 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.marketGetTickers (this.extend (request, params)); // // [ // { // "symbol":"68719476706", // "name":"ETH/BTC", // "base":"2", // "quote":"32", // "timestamp":1575905991933, // "datetime":"2019-12-09T15:39:51.933Z", // "high":"0", // "low":"0", // "open":"0", // "close":"0", // "last":"0", // "change":"0", // "percentage":"0", // "baseVolume":"0", // "quoteVolume":"0" // } // ] // if (Array.isArray (response)) { const ticker = this.safeValue (response, 0); if (ticker === undefined) { throw new BadResponse (this.id + ' fetchTicker() returned an empty response'); } return this.parseTicker (ticker, market); } return this.parseTicker (response, market); } parseTickers (rawTickers, symbols = undefined) { const tickers = []; for (let i = 0; i < rawTickers.length; i++) { tickers.push (this.parseTicker (rawTickers[i])); } return this.filterByArray (tickers, 'symbol', symbols); } async fetchBidsAsks (symbols = undefined, params = {}) { await this.loadMarkets (); const rawTickers = await this.marketGetDepth (params); return this.parseTickers (rawTickers, symbols); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const rawTickers = await this.marketGetTickers (params); return this.parseTickers (rawTickers, symbols); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0], parseFloat (ohlcv[1]), parseFloat (ohlcv[2]), parseFloat (ohlcv[3]), parseFloat (ohlcv[4]), parseFloat (ohlcv[5]), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'timeframe': this.timeframes[timeframe], }; if (since !== undefined) { request['since'] = since; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.marketGetKlines (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { const timestamp = this.safeInteger (trade, 'timestamp'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); const cost = this.safeFloat (trade, 'cost'); const id = this.safeString (trade, 'id'); const type = this.safeString (trade, 'type'); const takerOrMaker = this.safeString (trade, 'takerOrMaker'); const side = this.safeString (trade, 'side'); const datetime = this.safeString (trade, 'datetime'); const order = this.safeString (trade, 'order'); const fee = this.safeValue (trade, 'fee'); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (trade, 'symbol'); market = this.safeValue (this.markets_by_id, marketId); } if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'timestamp': timestamp, 'datetime': datetime, 'symbol': symbol, 'id': id, 'order': order, 'type': type, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (since !== undefined) { request['since'] = since; } if (limit !== undefined) { request['limit'] = limit; // default = 100, maximum = 500 } const response = await this.marketGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseOrder (order, market = undefined) { const status = this.safeString (order, 'status'); let symbol = undefined; const marketId = this.safeString (order, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const baseId = this.safeString (order, 'base'); const quoteId = this.safeString (order, 'quote'); if ((baseId !== undefined) && (quoteId !== undefined)) { const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.safeInteger (order, 'timestamp'); const datetime = this.safeString (order, 'datetime'); const lastTradeTimestamp = this.safeInteger (order, 'lastTradeTimestamp'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'filled'); const remaining = this.safeFloat (order, 'remaining'); const cost = this.safeFloat (order, 'cost'); const average = this.safeFloat (order, 'average'); const id = this.safeString (order, 'id'); const type = this.safeString (order, 'type'); const side = this.safeString (order, 'side'); const fee = this.safeValue (order, 'fee'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': datetime, 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { this.checkRequiredDependencies (); if (this.apiKey === undefined) { throw new ArgumentsRequired ('createOrder requires this.apiKey or userid in params'); } await this.loadMarkets (); const market = this.market (symbol); let sideNum = undefined; let typeNum = undefined; if (side === 'sell') { sideNum = 1; } else { sideNum = 2; } if (type === 'limit') { typeNum = 1; } else { typeNum = 2; price = 0; } const normalSymbol = market['normalSymbol']; const baseId = market['baseId']; const baseCurrency = this.currency (market['base']); const amountTruncated = this.amountToPrecision (symbol, amount); const amountChain = this.toWei (amountTruncated, baseCurrency['precision']['amount']); const quoteId = market['quoteId']; const quoteCurrency = this.currency (market['quote']); const priceRounded = this.priceToPrecision (symbol, price); const priceChain = this.toWei (priceRounded, quoteCurrency['precision']['amount']); const now = this.milliseconds (); const expiration = this.milliseconds (); let datetime = this.iso8601 (now); datetime = datetime.split ('.')[0]; let expirationDatetime = this.iso8601 (expiration); expirationDatetime = expirationDatetime.split ('.')[0]; const defaultDappId = 'Sagittarius'; const dappId = this.safeString (params, 'dappId', defaultDappId); const defaultFee = this.safeString (this.options, 'fee', '300000000000000'); const fee = this.safeString (params, 'fee', defaultFee); const eightBytes = this.integerPow ('2', '64'); const allByteStringArray = [ this.numberToBE (1, 32), this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (1, 1), this.numberToLE (Math.floor (expiration / 1000), 4), this.numberToLE (1, 1), this.numberToLE (32, 1), this.numberToLE (0, 8), this.numberToLE (fee, 8), // string for 32 bit php this.numberToLE (this.apiKey.length, 1), this.stringToBinary (this.encode (this.apiKey)), this.numberToLE (sideNum, 1), this.numberToLE (typeNum, 1), this.numberToLE (normalSymbol.length, 1), this.stringToBinary (this.encode (normalSymbol)), this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), this.numberToLE (this.integerDivide (priceChain, eightBytes), 8), this.numberToLE (this.integerModulo (priceChain, eightBytes), 8), this.numberToLE (0, 2), this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (Math.floor (expiration / 1000), 4), this.numberToLE (0, 2), this.numberToLE (parseInt (quoteId), 4), this.numberToLE (parseInt (baseId), 4), this.numberToLE (0, 1), this.numberToLE (1, 1), this.numberToLE (dappId.length, 1), this.stringToBinary (this.encode (dappId)), this.numberToLE (0, 1), ]; const txByteStringArray = [ this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (1, 1), this.numberToLE (Math.floor (expiration / 1000), 4), this.numberToLE (1, 1), this.numberToLE (32, 1), this.numberToLE (0, 8), this.numberToLE (fee, 8), // string for 32 bit php this.numberToLE (this.apiKey.length, 1), this.stringToBinary (this.encode (this.apiKey)), this.numberToLE (sideNum, 1), this.numberToLE (typeNum, 1), this.numberToLE (normalSymbol.length, 1), this.stringToBinary (this.encode (normalSymbol)), this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), this.numberToLE (this.integerDivide (priceChain, eightBytes), 8), this.numberToLE (this.integerModulo (priceChain, eightBytes), 8), this.numberToLE (0, 2), this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (Math.floor (expiration / 1000), 4), this.numberToLE (0, 2), this.numberToLE (parseInt (quoteId), 4), this.numberToLE (parseInt (baseId), 4), this.numberToLE (0, 1), this.numberToLE (1, 1), this.numberToLE (dappId.length, 1), this.stringToBinary (this.encode (dappId)), this.numberToLE (0, 1), ]; const txbytestring = this.binaryConcatArray (txByteStringArray); const txidhash = this.hash (txbytestring, 'sha256', 'hex'); const txid = txidhash.slice (0, 40); const orderidByteStringArray = [ this.numberToLE (txid.length, 1), this.stringToBinary (this.encode (txid)), this.numberToBE (0, 4), ]; const orderidbytestring = this.binaryConcatArray (orderidByteStringArray); const orderidhash = this.hash (orderidbytestring, 'sha256', 'hex'); const orderid = orderidhash.slice (0, 40); const bytestring = this.binaryConcatArray (allByteStringArray); const hash = this.hash (bytestring, 'sha256', 'hex'); const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); const mySignature = recoveryParam + signature['r'] + signature['s']; const operation = { 'now': datetime, 'expiration': expirationDatetime, 'fee': fee, 'creator': this.apiKey, 'side': sideNum, 'order_type': typeNum, 'market_name': normalSymbol, 'amount': amountChain, 'price': priceChain, 'use_btt_as_fee': false, 'money_id': parseInt (quoteId), 'stock_id': parseInt (baseId), }; const fatty = { 'timestamp': datetime, 'expiration': expirationDatetime, 'operations': [ [ 32, operation, ], ], 'validate_type': 0, 'dapp': dappId, 'signatures': [ mySignature, ], }; const request = { 'trObj': this.json (fatty), }; const response = await this.publicPostTransactionCreateorder (request); const timestamp = this.milliseconds (); const statusCode = this.safe_string (response, 'code'); const status = (statusCode === '0') ? 'open' : 'failed'; return { 'info': response, 'id': orderid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': undefined, 'type': undefined, 'side': undefined, 'price': undefined, 'amount': undefined, 'filled': undefined, 'remaining': undefined, 'cost': undefined, 'trades': undefined, 'fee': undefined, 'clientOrderId': undefined, 'average': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired ('fetchOrder requires this.apiKey or userid argument'); } await this.loadMarkets (); const request = { 'userid': this.apiKey, }; let market = undefined; if (symbol !== undefined) { market = this.markets[symbol]; request['symbol'] = market['id']; } request['id'] = id; const response = await this.publicGetOrders (this.extend (request, params)); return this.parseOrder (response, market); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired ('fetchOpenOrders requires this.apiKey or userid argument'); } await this.loadMarkets (); const request = { 'userid': this.apiKey, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrdersOpen (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired ('fetchClosedOrders requires this.apiKey or userid argument'); } await this.loadMarkets (); let market = undefined; const request = { 'userid': this.apiKey, }; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrdersClosed (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired ('fetchOrders requires this.apiKey or userid argument'); } await this.loadMarkets (); let market = undefined; const request = { 'userid': this.apiKey, }; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrdersAll (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } async cancelOrder (id, symbol = undefined, params = {}) { if (this.apiKey === undefined) { throw new ArgumentsRequired ('cancelOrder requires hasAlreadyAuthenticatedSuccessfully'); } if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const baseId = market['baseId']; const quoteId = market['quoteId']; const normalSymbol = market['normalSymbol']; const feeAmount = '300000000000000'; const now = this.milliseconds (); const expiration = 0; let datetime = this.iso8601 (now); datetime = datetime.split ('.')[0]; let expirationDatetime = this.iso8601 (expiration); expirationDatetime = expirationDatetime.split ('.')[0]; const defaultDappId = 'Sagittarius'; const dappId = this.safeString (params, 'dappId', defaultDappId); const byteStringArray = [ this.numberToBE (1, 32), this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (1, 1), this.numberToLE (expiration, 4), this.numberToLE (1, 1), this.numberToLE (33, 1), this.numberToLE (0, 8), this.numberToLE (feeAmount, 8), // string for 32 bit php this.numberToLE (this.apiKey.length, 1), this.stringToBinary (this.encode (this.apiKey)), this.numberToLE (normalSymbol.length, 1), this.stringToBinary (this.encode (normalSymbol)), this.base16ToBinary (id), this.numberToLE (parseInt (quoteId), 4), this.numberToLE (parseInt (baseId), 4), this.numberToLE (0, 1), this.numberToLE (1, 1), this.numberToLE (dappId.length, 1), this.stringToBinary (this.encode (dappId)), this.numberToLE (0, 1), ]; const bytestring = this.binaryConcatArray (byteStringArray); const hash = this.hash (bytestring, 'sha256', 'hex'); const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); const mySignature = recoveryParam + signature['r'] + signature['s']; const operation = { 'fee': feeAmount, 'creator': this.apiKey, 'order_id': id, 'market_name': normalSymbol, 'money_id': parseInt (quoteId), 'stock_id': parseInt (baseId), }; const fatty = { 'timestamp': datetime, 'expiration': expirationDatetime, 'operations': [ [ 33, operation, ], ], 'validate_type': 0, 'dapp': dappId, 'signatures': [ mySignature, ], }; const request = { 'trObj': this.json (fatty), }; const response = await this.publicPostTransactionCancelorder (request); const timestamp = this.milliseconds (); const statusCode = this.safe_string (response, 'code'); const status = (statusCode === '0') ? 'canceled' : 'failed'; return { 'info': response, 'id': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': undefined, 'type': undefined, 'side': undefined, 'price': undefined, 'amount': undefined, 'filled': undefined, 'remaining': undefined, 'cost': undefined, 'trades': undefined, 'fee': undefined, 'clientOrderId': undefined, 'average': undefined, }; } async transfer (code, amount, address, message = '', params = {}) { this.checkRequiredDependencies (); if (this.apiKey === undefined) { throw new ArgumentsRequired ('transfer requires this.apiKey'); } await this.loadMarkets (); const currency = this.currency (code); const amountTruncate = this.decimalToPrecision (amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING); const amountChain = this.toWei (amountTruncate, currency['precision']['amount']); const assetType = parseInt (currency['id']); const now = this.milliseconds (); const expiration = now; let datetime = this.iso8601 (now); datetime = datetime.split ('.')[0]; let expirationDatetime = this.iso8601 (expiration); expirationDatetime = expirationDatetime.split ('.')[0]; const feeAmount = '300000000000000'; const defaultDappId = 'Sagittarius'; const dappId = this.safeString (params, 'dappId', defaultDappId); const eightBytes = this.integerPow ('2', '64'); const byteStringArray = [ this.numberToBE (1, 32), this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (1, 1), this.numberToLE (Math.floor (expiration / 1000), 4), this.numberToLE (1, 1), this.numberToLE (28, 1), this.numberToLE (0, 8), this.numberToLE (feeAmount, 8), // string for 32 bit php this.numberToLE (this.apiKey.length, 1), this.stringToBinary (this.encode (this.apiKey)), this.numberToLE (address.length, 1), this.stringToBinary (this.encode (address)), this.numberToLE (assetType, 4), this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), this.numberToLE (1, 1), this.numberToLE (message.length, 1), this.stringToBinary (this.encode (message)), this.numberToLE (0, 1), this.numberToLE (1, 1), this.numberToLE (dappId.length, 1), this.stringToBinary (this.encode (dappId)), this.numberToLE (0, 1), ]; const bytestring = this.binaryConcatArray (byteStringArray); const hash = this.hash (bytestring, 'sha256', 'hex'); const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); const mySignature = recoveryParam + signature['r'] + signature['s']; const operation = { 'fee': '300000000000000', 'from': this.apiKey, 'to': address, 'asset_type': parseInt (currency['id']), 'amount': amountChain.toString (), 'message': message, }; const fatty = { 'timestamp': datetime, 'expiration': expirationDatetime, 'operations': [ [ 28, operation, ], ], 'validate_type': 0, 'dapp': dappId, 'signatures': [ mySignature, ], }; const request = { 'trObj': this.json (fatty), }; const response = await this.publicPostTransactionTransfer (request); const timestamp = this.milliseconds (); const statusCode = this.safe_string (response, 'code'); let status = ''; if (statusCode === '0') { status = 'submit success'; } else { status = 'submit fail'; } return { 'info': response, 'id': '', 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': undefined, 'type': undefined, 'side': undefined, 'price': undefined, 'amount': undefined, 'filled': undefined, 'remaining': undefined, 'cost': undefined, 'fee': undefined, 'clientOrderId': undefined, 'average': undefined, 'trades': undefined, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired ('fetchMyTrades requires this.apiKey or userid argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'userid': this.apiKey, }; if (symbol !== undefined) { request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrdersTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired ('fetchDeposits requires this.apiKey or userid argument'); } let currency = undefined; const request = { 'userid': this.apiKey, }; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (since !== undefined) { request['since'] = since; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetDeposits (this.extend (request, params)); return this.parseTransactions (response, currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired ('fetchWithdrawals requires this.apiKey or userid argument'); } let currency = undefined; const request = { 'userid': this.apiKey, }; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (since !== undefined) { request['since'] = since; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetWithdrawals (this.extend (request, params)); return this.parseTransactions (response, currency, since, limit); } parseTransactionStatus (status) { const statuses = { 'DEPOSIT_FAILED': 'failed', 'FEE_SEND_FAILED': 'failed', 'FEE_FAILED': 'failed', 'PAY_SEND_FAILED': 'failed', 'PAY_FAILED': 'failed', 'BTT_FAILED': 'failed', 'WITHDDRAW_FAILED': 'failed', 'USER_FAILED': 'failed', 'FEE_EXECUED': 'pending', 'PAY_EXECUED': 'pending', 'WITHDDRAW_EXECUTED': 'pending', 'USER_EXECUED': 'pending', 'BTT_SUCCED': 'ok', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { const id = this.safeString (transaction, 'id'); const address = this.safeString (transaction, 'address'); let tag = this.safeString (transaction, 'tag'); if (tag !== undefined) { if (tag.length < 1) { tag = undefined; } } const txid = this.safeValue (transaction, 'txid'); const currencyId = this.safeString (transaction, 'code'); const code = this.safeCurrencyCode (currencyId, currency); const timestamp = this.safeInteger (transaction, 'timestamp'); const datetime = this.safeString (transaction, 'datetime'); const type = this.safeString (transaction, 'type'); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const amount = this.safeFloat (transaction, 'amount'); const feeInfo = this.safeValue (transaction, 'fee'); const feeCost = this.safeFloat (feeInfo, 'cost'); const feeCurrencyId = this.safeString (feeInfo, 'code'); const feeCode = this.safeCurrencyCode (feeCurrencyId, currency); const fee = { 'cost': feeCost, 'currency': feeCode, }; return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': datetime, 'address': address, 'tag': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': fee, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); if (!('userid' in params) && (this.apiKey === undefined)) { throw new ArgumentsRequired ('fetchDepositAddress requires this.apiKey or userid argument'); } const currency = this.currency (code); const request = { 'userid': this.apiKey, 'code': currency['id'], }; const response = await this.publicGetDepositaddress (request); const address = this.safeString (response[0], 'address'); const tag = this.safeString (response[0], 'tag'); const chainType = this.safeString (response[0], 'chainType'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'chainType': chainType, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkRequiredDependencies (); this.checkAddress (address); await this.loadMarkets (); if (this.apiKey === undefined) { throw new ArgumentsRequired ('withdraw requires this.apiKey'); } const addressResponse = await this.fetchDepositAddress (code); const chainTypeString = this.safeString (addressResponse, 'chainType'); const chainId = this.safeString (addressResponse['info'][0], 'chainId'); let middleAddress = ''; if (chainTypeString === 'eos') { middleAddress = address; } else { middleAddress = this.safeString (addressResponse, 'address'); } let operationId = 18; if (chainTypeString !== 'ethereum' && chainTypeString !== 'etc' && chainTypeString !== 'eos' && chainTypeString !== 'cmt' && chainTypeString !== 'naka') { operationId = 26; } const now = this.milliseconds (); const expiration = 0; let datetime = this.iso8601 (now); datetime = datetime.split ('.')[0]; let expirationDatetime = this.iso8601 (expiration); expirationDatetime = expirationDatetime.split ('.')[0]; const defaultDappId = 'Sagittarius'; const dappId = this.safeString (params, 'dappId', defaultDappId); const feeAmount = '300000000000000'; const currency = this.currency (code); const coinId = currency['id']; const amountTruncate = this.decimalToPrecision (amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING); const amountChain = this.toWei (amountTruncate, currency['info']['externalPrecision']); const eightBytes = this.integerPow ('2', '64'); let assetFee = 0; let byteStringArray = []; if (operationId === 26) { assetFee = currency['info']['fee']; byteStringArray = [ this.numberToBE (1, 32), this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (1, 1), this.numberToLE (Math.floor (expiration / 1000), 4), this.numberToLE (1, 1), this.numberToLE (operationId, 1), this.numberToLE (0, 8), this.numberToLE (feeAmount, 8), // string for 32 bit php this.numberToLE (this.apiKey.length, 1), this.stringToBinary (this.encode (this.apiKey)), this.numberToLE (address.length, 1), this.stringToBinary (this.encode (address)), this.numberToLE (parseInt (coinId), 4), this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), this.numberToLE (1, 1), this.numberToLE (this.integerDivide (assetFee, eightBytes), 8), this.numberToLE (this.integerModulo (assetFee, eightBytes), 8), this.numberToLE (0, 1), this.numberToLE (1, 1), this.numberToLE (dappId.length, 1), this.stringToBinary (this.encode (dappId)), this.numberToLE (0, 1), ]; } else { byteStringArray = [ this.numberToBE (1, 32), this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (1, 1), this.numberToLE (Math.floor (expiration / 1000), 4), this.numberToLE (1, 1), this.numberToLE (operationId, 1), this.numberToLE (0, 8), this.numberToLE (feeAmount, 8), // string for 32 bit php this.numberToLE (this.apiKey.length, 1), this.stringToBinary (this.encode (this.apiKey)), this.numberToLE (Math.floor (now / 1000), 4), this.numberToLE (1, 1), this.numberToLE (4, 1), this.numberToLE (0, 8), this.numberToLE (feeAmount, 8), this.numberToLE (this.apiKey.length, 1), this.stringToBinary (this.encode (this.apiKey)), this.numberToLE (middleAddress.length, 1), this.stringToBinary (this.encode (middleAddress)), this.numberToLE (parseInt (coinId), 4), this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), this.numberToLE (0, 1), this.numberToLE (1, 1), this.numberToLE (dappId.length, 1), this.stringToBinary (this.encode (dappId)), this.numberToLE (0, 1), ]; } const bytestring = this.binaryConcatArray (byteStringArray); const hash = this.hash (bytestring, 'sha256', 'hex'); const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); const mySignature = recoveryParam + signature['r'] + signature['s']; let fatty = undefined; let request = undefined; let operation = undefined; const chainContractAddress = this.safeString (currency['info'], 'chainContractAddress'); if (operationId === 26) { operation = { 'fee': feeAmount, 'from': this.apiKey, 'to_external_address': address, 'asset_type': parseInt (coinId), 'amount': amountChain, 'asset_fee': assetFee, }; fatty = { 'timestamp': datetime, 'expiration': expirationDatetime, 'operations': [ [ operationId, operation, ], ], 'validate_type': 0, 'dapp': dappId, 'signatures': [ mySignature, ], }; request = { 'chainType': chainId, 'trObj': this.json (fatty), 'chainContractAddress': chainContractAddress, }; } else { operation = { 'fee': feeAmount, 'from': this.apiKey, 'to_external_address': middleAddress, 'asset_type': parseInt (coinId), 'amount': amountChain, 'asset_fee': assetFee, }; const middle = { 'fee': feeAmount, 'proposaler': this.apiKey, 'expiration_time': datetime, 'proposed_ops': [{ 'op': [4, operation], }], }; fatty = { 'timestamp': datetime, 'expiration': expirationDatetime, 'operations': [ [ operationId, middle, ], ], 'validate_type': 0, 'dapp': dappId, 'signatures': [ mySignature, ], }; if (chainTypeString === 'eos') { request = { 'chainType': chainId, 'toExternalAddress': 'noneed', 'trObj': this.json (fatty), 'chainContractAddress': chainContractAddress, }; } else { request = { 'chainType': chainId, 'toExternalAddress': address, 'trObj': this.json (fatty), 'chainContractAddress': chainContractAddress, }; } } const response = await this.publicPostTransactionWithdraw (request); return { 'info': response, 'id': this.safeString (response, 'id'), }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; url += '/' + path; if (Object.keys (params).length) { url += '?' + this.urlencode (params); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (code === 503) { throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); } if (response === undefined) { return; // fallback to default error handler } if ('code' in response) { const status = this.safeString (response, 'code'); if (status === '1') { const message = this.safeString (response, 'msg'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, message, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],58:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, AuthenticationError, NullResponse, InvalidOrder, NotSupported, InsufficientFunds, InvalidNonce, OrderNotFound, RateLimitExceeded } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class cex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'cex', 'name': 'CEX.IO', 'countries': [ 'GB', 'EU', 'CY', 'RU' ], 'rateLimit': 1500, 'has': { 'CORS': false, 'fetchCurrencies': true, 'fetchTickers': true, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchDepositAddress': true, 'fetchOrders': true, }, 'timeframes': { '1m': '1m', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg', 'api': 'https://cex.io/api', 'www': 'https://cex.io', 'doc': 'https://cex.io/cex-api', 'fees': [ 'https://cex.io/fee-schedule', 'https://cex.io/limits-commissions', ], 'referral': 'https://cex.io/r/0/up105393824/0/', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': true, }, 'api': { 'public': { 'get': [ 'currency_profile', 'currency_limits/', 'last_price/{pair}/', 'last_prices/{currencies}/', 'ohlcv/hd/{yyyymmdd}/{pair}', 'order_book/{pair}/', 'ticker/{pair}/', 'tickers/{currencies}/', 'trade_history/{pair}/', ], 'post': [ 'convert/{pair}', 'price_stats/{pair}', ], }, 'private': { 'post': [ 'active_orders_status/', 'archived_orders/{pair}/', 'balance/', 'cancel_order/', 'cancel_orders/{pair}/', 'cancel_replace_order/{pair}/', 'close_position/{pair}/', 'get_address/', 'get_myfee/', 'get_order/', 'get_order_tx/', 'open_orders/{pair}/', 'open_orders/', 'open_position/{pair}/', 'open_positions/{pair}/', 'place_order/{pair}/', ], }, }, 'fees': { 'trading': { 'maker': 0.16 / 100, 'taker': 0.25 / 100, }, 'funding': { 'withdraw': { // 'USD': undefined, // 'EUR': undefined, // 'RUB': undefined, // 'GBP': undefined, 'BTC': 0.001, 'ETH': 0.01, 'BCH': 0.001, 'DASH': 0.01, 'BTG': 0.001, 'ZEC': 0.001, 'XRP': 0.02, }, 'deposit': { // 'USD': amount => amount * 0.035 + 0.25, // 'EUR': amount => amount * 0.035 + 0.24, // 'RUB': amount => amount * 0.05 + 15.57, // 'GBP': amount => amount * 0.035 + 0.2, 'BTC': 0.0, 'ETH': 0.0, 'BCH': 0.0, 'DASH': 0.0, 'BTG': 0.0, 'ZEC': 0.0, 'XRP': 0.0, 'XLM': 0.0, }, }, }, 'exceptions': { 'exact': {}, 'broad': { 'Insufficient funds': InsufficientFunds, 'Nonce must be incremented': InvalidNonce, 'Invalid Order': InvalidOrder, 'Order not found': OrderNotFound, 'Rate limit exceeded': RateLimitExceeded, 'Invalid API key': AuthenticationError, 'There was an error while placing your order': InvalidOrder, }, }, 'options': { 'fetchOHLCVWarning': true, 'createMarketBuyOrderRequiresPrice': true, 'order': { 'status': { 'c': 'canceled', 'd': 'closed', 'cd': 'closed', 'a': 'open', }, }, }, }); } async fetchCurrenciesFromCache (params = {}) { // this method is now redundant // currencies are now fetched before markets const options = this.safeValue (this.options, 'fetchCurrencies', {}); const timestamp = this.safeInteger (options, 'timestamp'); const expires = this.safeInteger (options, 'expires', 1000); const now = this.milliseconds (); if ((timestamp === undefined) || ((now - timestamp) > expires)) { const response = await this.publicGetCurrencyProfile (params); this.options['fetchCurrencies'] = this.extend (options, { 'response': response, 'timestamp': now, }); } return this.safeValue (this.options['fetchCurrencies'], 'response'); } async fetchCurrencies (params = {}) { const response = await this.fetchCurrenciesFromCache (params); this.options['currencies'] = { 'timestamp': this.milliseconds (), 'response': response, }; // // { // "e":"currency_profile", // "ok":"ok", // "data":{ // "symbols":[ // { // "code":"GHS", // "contract":true, // "commodity":true, // "fiat":false, // "description":"CEX.IO doesn't provide cloud mining services anymore.", // "precision":8, // "scale":0, // "minimumCurrencyAmount":"0.00000001", // "minimalWithdrawalAmount":-1 // }, // { // "code":"BTC", // "contract":false, // "commodity":false, // "fiat":false, // "description":"", // "precision":8, // "scale":0, // "minimumCurrencyAmount":"0.00000001", // "minimalWithdrawalAmount":0.002 // }, // { // "code":"ETH", // "contract":false, // "commodity":false, // "fiat":false, // "description":"", // "precision":8, // "scale":2, // "minimumCurrencyAmount":"0.00000100", // "minimalWithdrawalAmount":0.01 // } // ], // "pairs":[ // { // "symbol1":"BTC", // "symbol2":"USD", // "pricePrecision":1, // "priceScale":"/1000000", // "minLotSize":0.002, // "minLotSizeS2":20 // }, // { // "symbol1":"ETH", // "symbol2":"USD", // "pricePrecision":2, // "priceScale":"/10000", // "minLotSize":0.1, // "minLotSizeS2":20 // } // ] // } // } // const data = this.safeValue (response, 'data', []); const currencies = this.safeValue (data, 'symbols', []); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'code'); const code = this.safeCurrencyCode (id); const precision = this.safeInteger (currency, 'precision'); const active = true; result[code] = { 'id': id, 'code': code, 'name': id, 'active': active, 'precision': precision, 'fee': undefined, 'limits': { 'amount': { 'min': this.safeFloat (currency, 'minimumCurrencyAmount'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'minimalWithdrawalAmount'), 'max': undefined, }, }, 'info': currency, }; } return result; } async fetchMarkets (params = {}) { const currenciesResponse = await this.fetchCurrenciesFromCache (params); const currenciesData = this.safeValue (currenciesResponse, 'data', {}); const currencies = this.safeValue (currenciesData, 'symbols', []); const currenciesById = this.indexBy (currencies, 'code'); const pairs = this.safeValue (currenciesData, 'pairs', []); const response = await this.publicGetCurrencyLimits (params); // // { // "e":"currency_limits", // "ok":"ok", // "data": { // "pairs":[ // { // "symbol1":"BTC", // "symbol2":"USD", // "minLotSize":0.002, // "minLotSizeS2":20, // "maxLotSize":30, // "minPrice":"1500", // "maxPrice":"35000" // }, // { // "symbol1":"BCH", // "symbol2":"EUR", // "minLotSize":0.1, // "minLotSizeS2":20, // "maxLotSize":null, // "minPrice":"25", // "maxPrice":"8192" // } // ] // } // } // const result = []; const markets = this.safeValue (response['data'], 'pairs'); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const baseId = this.safeString (market, 'symbol1'); const quoteId = this.safeString (market, 'symbol2'); const id = baseId + '/' + quoteId; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const baseCurrency = this.safeValue (currenciesById, baseId, {}); const quoteCurrency = this.safeValue (currenciesById, quoteId, {}); let pricePrecision = this.safeInteger (quoteCurrency, 'precision', 8); for (let j = 0; j < pairs.length; j++) { const pair = pairs[j]; if ((pair['symbol1'] === baseId) && (pair['symbol2'] === quoteId)) { // we might need to account for `priceScale` here pricePrecision = this.safeInteger (pair, 'pricePrecision', pricePrecision); } } const baseCcyPrecision = this.safeInteger (baseCurrency, 'precision', 8); const baseCcyScale = this.safeInteger (baseCurrency, 'scale', 0); const amountPrecision = baseCcyPrecision - baseCcyScale; const precision = { 'amount': amountPrecision, 'price': pricePrecision, }; result.push ({ 'id': id, 'info': market, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minLotSize'), 'max': this.safeFloat (market, 'maxLotSize'), }, 'price': { 'min': this.safeFloat (market, 'minPrice'), 'max': this.safeFloat (market, 'maxPrice'), }, 'cost': { 'min': this.safeFloat (market, 'minLotSizeS2'), 'max': undefined, }, }, 'active': undefined, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostBalance (params); const result = { 'info': response }; const ommited = [ 'username', 'timestamp' ]; const balances = this.omit (response, ommited); const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const balance = this.safeValue (balances, currencyId, {}); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); // https://github.com/ccxt/ccxt/issues/5484 account['used'] = this.safeFloat (balance, 'orders', 0.0); const code = this.safeCurrencyCode (currencyId); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; if (limit !== undefined) { request['depth'] = limit; } const response = await this.publicGetOrderBookPair (this.extend (request, params)); const timestamp = this.safeTimestamp (response, 'timestamp'); return this.parseOrderBook (response, timestamp); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, ohlcv[1], ohlcv[2], ohlcv[3], ohlcv[4], ohlcv[5], ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (since === undefined) { since = this.milliseconds () - 86400000; // yesterday } else { if (this.options['fetchOHLCVWarning']) { throw new ExchangeError (this.id + " fetchOHLCV warning: CEX can return historical candles for a certain date only, this might produce an empty or null reply. Set exchange.options['fetchOHLCVWarning'] = false or add ({ 'options': { 'fetchOHLCVWarning': false }}) to constructor params to suppress this warning message."); } } let ymd = this.ymd (since); ymd = ymd.split ('-'); ymd = ymd.join (''); const request = { 'pair': market['id'], 'yyyymmdd': ymd, }; try { const response = await this.publicGetOhlcvHdYyyymmddPair (this.extend (request, params)); const key = 'data' + this.timeframes[timeframe]; const ohlcvs = JSON.parse (response[key]); return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } catch (e) { if (e instanceof NullResponse) { return []; } } } parseTicker (ticker, market = undefined) { const timestamp = this.safeTimestamp (ticker, 'timestamp'); const volume = this.safeFloat (ticker, 'volume'); const high = this.safeFloat (ticker, 'high'); const low = this.safeFloat (ticker, 'low'); const bid = this.safeFloat (ticker, 'bid'); const ask = this.safeFloat (ticker, 'ask'); const last = this.safeFloat (ticker, 'last'); let symbol = undefined; if (market) { symbol = market['symbol']; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': high, 'low': low, 'bid': bid, 'bidVolume': undefined, 'ask': ask, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': volume, 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const currencies = Object.keys (this.currencies); const request = { 'currencies': currencies.join ('/'), }; const response = await this.publicGetTickersCurrencies (this.extend (request, params)); const tickers = response['data']; const result = {}; for (let t = 0; t < tickers.length; t++) { const ticker = tickers[t]; const symbol = ticker['pair'].replace (':', '/'); const market = this.markets[symbol]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const ticker = await this.publicGetTickerPair (this.extend (request, params)); return this.parseTicker (ticker, market); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const id = this.safeString (trade, 'tid'); const type = undefined; const side = this.safeString (trade, 'type'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetTradeHistoryPair (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { // for market buy it requires the amount of quote currency to spend if ((type === 'market') && (side === 'buy')) { if (this.options['createMarketBuyOrderRequiresPrice']) { if (price === undefined) { throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); } else { amount = amount * price; } } } await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), 'type': side, 'amount': amount, }; if (type === 'limit') { request['price'] = price; } else { request['order_type'] = type; } const response = await this.privatePostPlaceOrderPair (this.extend (request, params)); // // { // "id": "12978363524", // "time": 1586610022259, // "type": "buy", // "price": "0.033934", // "amount": "0.10722802", // "pending": "0.10722802", // "complete": false // } // const placedAmount = this.safeFloat (response, 'amount'); const remaining = this.safeFloat (response, 'pending'); const timestamp = this.safeValue (response, 'time'); const complete = this.safeValue (response, 'complete'); const status = complete ? 'closed' : 'open'; let filled = undefined; if ((placedAmount !== undefined) && (remaining !== undefined)) { filled = Math.max (placedAmount - remaining, 0); } return { 'id': this.safeString (response, 'id'), 'info': response, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'type': type, 'side': this.safeString (response, 'type'), 'symbol': symbol, 'status': status, 'price': this.safeFloat (response, 'price'), 'amount': placedAmount, 'cost': undefined, 'average': undefined, 'remaining': remaining, 'filled': filled, 'fee': undefined, 'trades': undefined, }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; return await this.privatePostCancelOrder (this.extend (request, params)); } parseOrder (order, market = undefined) { // Depending on the call, 'time' can be a unix int, unix string or ISO string // Yes, really let timestamp = this.safeValue (order, 'time'); if (typeof timestamp === 'string' && timestamp.indexOf ('T') >= 0) { // ISO8601 string timestamp = this.parse8601 (timestamp); } else { // either integer or string integer timestamp = parseInt (timestamp); } let symbol = undefined; if (market === undefined) { const baseId = this.safeString (order, 'symbol1'); const quoteId = this.safeString (order, 'symbol2'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; if (symbol in this.markets) { market = this.market (symbol); } } const status = this.parseOrderStatus (this.safeString (order, 'status')); const price = this.safeFloat (order, 'price'); let amount = this.safeFloat (order, 'amount'); // sell orders can have a negative amount // https://github.com/ccxt/ccxt/issues/5338 if (amount !== undefined) { amount = Math.abs (amount); } const remaining = this.safeFloat2 (order, 'pending', 'remains'); const filled = amount - remaining; let fee = undefined; let cost = undefined; if (market !== undefined) { symbol = market['symbol']; cost = this.safeFloat (order, 'ta:' + market['quote']); if (cost === undefined) { cost = this.safeFloat (order, 'tta:' + market['quote']); } const baseFee = 'fa:' + market['base']; const baseTakerFee = 'tfa:' + market['base']; const quoteFee = 'fa:' + market['quote']; const quoteTakerFee = 'tfa:' + market['quote']; let feeRate = this.safeFloat (order, 'tradingFeeMaker'); if (!feeRate) { feeRate = this.safeFloat (order, 'tradingFeeTaker', feeRate); } if (feeRate) { feeRate /= 100.0; // convert to mathematically-correct percentage coefficients: 1.0 = 100% } if ((baseFee in order) || (baseTakerFee in order)) { const baseFeeCost = this.safeFloat2 (order, baseFee, baseTakerFee); fee = { 'currency': market['base'], 'rate': feeRate, 'cost': baseFeeCost, }; } else if ((quoteFee in order) || (quoteTakerFee in order)) { const quoteFeeCost = this.safeFloat2 (order, quoteFee, quoteTakerFee); fee = { 'currency': market['quote'], 'rate': feeRate, 'cost': quoteFeeCost, }; } } if (!cost) { cost = price * filled; } const side = order['type']; let trades = undefined; const orderId = order['id']; if ('vtx' in order) { trades = []; for (let i = 0; i < order['vtx'].length; i++) { const item = order['vtx'][i]; const tradeSide = this.safeString (item, 'type'); if (tradeSide === 'cancel') { // looks like this might represent the cancelled part of an order // { id: '4426729543', // type: 'cancel', // time: '2017-09-22T00:24:30.476Z', // user: 'up106404164', // c: 'user:up106404164:a:BCH', // d: 'order:4426728375:a:BCH', // a: '0.09935956', // amount: '0.09935956', // balance: '0.42580261', // symbol: 'BCH', // order: '4426728375', // buy: null, // sell: null, // pair: null, // pos: null, // cs: '0.42580261', // ds: 0 } continue; } const tradePrice = this.safeFloat (item, 'price'); if (tradePrice === undefined) { // this represents the order // { // "a": "0.47000000", // "c": "user:up106404164:a:EUR", // "d": "order:6065499239:a:EUR", // "cs": "1432.93", // "ds": "476.72", // "id": "6065499249", // "buy": null, // "pos": null, // "pair": null, // "sell": null, // "time": "2018-04-22T13:07:22.152Z", // "type": "buy", // "user": "up106404164", // "order": "6065499239", // "amount": "-715.97000000", // "symbol": "EUR", // "balance": "1432.93000000" } continue; } // todo: deal with these if (tradeSide === 'costsNothing') { continue; } // -- // if (side !== tradeSide) // throw new Error (JSON.stringify (order, null, 2)); // if (orderId !== item['order']) // throw new Error (JSON.stringify (order, null, 2)); // -- // partial buy trade // { // "a": "0.01589885", // "c": "user:up106404164:a:BTC", // "d": "order:6065499239:a:BTC", // "cs": "0.36300000", // "ds": 0, // "id": "6067991213", // "buy": "6065499239", // "pos": null, // "pair": null, // "sell": "6067991206", // "time": "2018-04-22T23:09:11.773Z", // "type": "buy", // "user": "up106404164", // "order": "6065499239", // "price": 7146.5, // "amount": "0.01589885", // "symbol": "BTC", // "balance": "0.36300000", // "symbol2": "EUR", // "fee_amount": "0.19" } // -- // trade with zero amount, but non-zero fee // { // "a": "0.00000000", // "c": "user:up106404164:a:EUR", // "d": "order:5840654423:a:EUR", // "cs": 559744, // "ds": 0, // "id": "5840654429", // "buy": "5807238573", // "pos": null, // "pair": null, // "sell": "5840654423", // "time": "2018-03-15T03:20:14.010Z", // "type": "sell", // "user": "up106404164", // "order": "5840654423", // "price": 730, // "amount": "0.00000000", // "symbol": "EUR", // "balance": "5597.44000000", // "symbol2": "BCH", // "fee_amount": "0.01" } // -- // trade which should have an amount of exactly 0.002BTC // { // "a": "16.70000000", // "c": "user:up106404164:a:GBP", // "d": "order:9927386681:a:GBP", // "cs": "86.90", // "ds": 0, // "id": "9927401610", // "buy": "9927401601", // "pos": null, // "pair": null, // "sell": "9927386681", // "time": "2019-08-21T15:25:37.777Z", // "type": "sell", // "user": "up106404164", // "order": "9927386681", // "price": 8365, // "amount": "16.70000000", // "office": "UK", // "symbol": "GBP", // "balance": "86.90000000", // "symbol2": "BTC", // "fee_amount": "0.03" // } const tradeTimestamp = this.parse8601 (this.safeString (item, 'time')); const tradeAmount = this.safeFloat (item, 'amount'); const feeCost = this.safeFloat (item, 'fee_amount'); let absTradeAmount = (tradeAmount < 0) ? -tradeAmount : tradeAmount; let tradeCost = undefined; if (tradeSide === 'sell') { tradeCost = absTradeAmount; absTradeAmount = this.sum (feeCost, tradeCost) / tradePrice; } else { tradeCost = absTradeAmount * tradePrice; } trades.push ({ 'id': this.safeString (item, 'id'), 'timestamp': tradeTimestamp, 'datetime': this.iso8601 (tradeTimestamp), 'order': orderId, 'symbol': symbol, 'price': tradePrice, 'amount': absTradeAmount, 'cost': tradeCost, 'side': tradeSide, 'fee': { 'cost': feeCost, 'currency': market['quote'], }, 'info': item, 'type': undefined, 'takerOrMaker': undefined, }); } } return { 'id': orderId, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': (price === undefined) ? 'market' : 'limit', 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': trades, 'fee': fee, 'info': order, 'average': undefined, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let method = 'privatePostOpenOrders'; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; method += 'Pair'; } const orders = await this[method] (this.extend (request, params)); for (let i = 0; i < orders.length; i++) { orders[i] = this.extend (orders[i], { 'status': 'open' }); } return this.parseOrders (orders, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const method = 'privatePostArchivedOrdersPair'; if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a symbol argument'); } const market = this.market (symbol); const request = { 'pair': market['id'] }; const response = await this[method] (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id.toString (), }; const response = await this.privatePostGetOrderTx (this.extend (request, params)); return this.parseOrder (response['data']); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'limit': limit, 'pair': market['id'], 'dateFrom': since, }; const response = await this.privatePostArchivedOrdersPair (this.extend (request, params)); const results = []; for (let i = 0; i < response.length; i++) { // cancelled (unfilled): // { id: '4005785516', // type: 'sell', // time: '2017-07-18T19:08:34.223Z', // lastTxTime: '2017-07-18T19:08:34.396Z', // lastTx: '4005785522', // pos: null, // status: 'c', // symbol1: 'ETH', // symbol2: 'GBP', // amount: '0.20000000', // price: '200.5625', // remains: '0.20000000', // 'a:ETH:cds': '0.20000000', // tradingFeeMaker: '0', // tradingFeeTaker: '0.16', // tradingFeeUserVolumeAmount: '10155061217', // orderId: '4005785516' } // -- // cancelled (partially filled buy): // { id: '4084911657', // type: 'buy', // time: '2017-08-05T03:18:39.596Z', // lastTxTime: '2019-03-19T17:37:46.404Z', // lastTx: '8459265833', // pos: null, // status: 'cd', // symbol1: 'BTC', // symbol2: 'GBP', // amount: '0.05000000', // price: '2241.4692', // tfacf: '1', // remains: '0.03910535', // 'tfa:GBP': '0.04', // 'tta:GBP': '24.39', // 'a:BTC:cds': '0.01089465', // 'a:GBP:cds': '112.26', // 'f:GBP:cds': '0.04', // tradingFeeMaker: '0', // tradingFeeTaker: '0.16', // tradingFeeUserVolumeAmount: '13336396963', // orderId: '4084911657' } // -- // cancelled (partially filled sell): // { id: '4426728375', // type: 'sell', // time: '2017-09-22T00:24:20.126Z', // lastTxTime: '2017-09-22T00:24:30.476Z', // lastTx: '4426729543', // pos: null, // status: 'cd', // symbol1: 'BCH', // symbol2: 'BTC', // amount: '0.10000000', // price: '0.11757182', // tfacf: '1', // remains: '0.09935956', // 'tfa:BTC': '0.00000014', // 'tta:BTC': '0.00007537', // 'a:BCH:cds': '0.10000000', // 'a:BTC:cds': '0.00007537', // 'f:BTC:cds': '0.00000014', // tradingFeeMaker: '0', // tradingFeeTaker: '0.18', // tradingFeeUserVolumeAmount: '3466715450', // orderId: '4426728375' } // -- // filled: // { id: '5342275378', // type: 'sell', // time: '2018-01-04T00:28:12.992Z', // lastTxTime: '2018-01-04T00:28:12.992Z', // lastTx: '5342275393', // pos: null, // status: 'd', // symbol1: 'BCH', // symbol2: 'BTC', // amount: '0.10000000', // kind: 'api', // price: '0.17', // remains: '0.00000000', // 'tfa:BTC': '0.00003902', // 'tta:BTC': '0.01699999', // 'a:BCH:cds': '0.10000000', // 'a:BTC:cds': '0.01699999', // 'f:BTC:cds': '0.00003902', // tradingFeeMaker: '0.15', // tradingFeeTaker: '0.23', // tradingFeeUserVolumeAmount: '1525951128', // orderId: '5342275378' } // -- // market order (buy): // { "id": "6281946200", // "pos": null, // "time": "2018-05-23T11:55:43.467Z", // "type": "buy", // "amount": "0.00000000", // "lastTx": "6281946210", // "status": "d", // "amount2": "20.00", // "orderId": "6281946200", // "remains": "0.00000000", // "symbol1": "ETH", // "symbol2": "EUR", // "tfa:EUR": "0.05", // "tta:EUR": "19.94", // "a:ETH:cds": "0.03764100", // "a:EUR:cds": "20.00", // "f:EUR:cds": "0.05", // "lastTxTime": "2018-05-23T11:55:43.467Z", // "tradingFeeTaker": "0.25", // "tradingFeeUserVolumeAmount": "55998097" } // -- // market order (sell): // { "id": "6282200948", // "pos": null, // "time": "2018-05-23T12:42:58.315Z", // "type": "sell", // "amount": "-0.05000000", // "lastTx": "6282200958", // "status": "d", // "orderId": "6282200948", // "remains": "0.00000000", // "symbol1": "ETH", // "symbol2": "EUR", // "tfa:EUR": "0.07", // "tta:EUR": "26.49", // "a:ETH:cds": "0.05000000", // "a:EUR:cds": "26.49", // "f:EUR:cds": "0.07", // "lastTxTime": "2018-05-23T12:42:58.315Z", // "tradingFeeTaker": "0.25", // "tradingFeeUserVolumeAmount": "56294576" } const order = response[i]; const status = this.parseOrderStatus (this.safeString (order, 'status')); const baseId = this.safeString (order, 'symbol1'); const quoteId = this.safeString (order, 'symbol2'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const side = this.safeString (order, 'type'); const baseAmount = this.safeFloat (order, 'a:' + baseId + ':cds'); const quoteAmount = this.safeFloat (order, 'a:' + quoteId + ':cds'); const fee = this.safeFloat (order, 'f:' + quoteId + ':cds'); const amount = this.safeFloat (order, 'amount'); const price = this.safeFloat (order, 'price'); const remaining = this.safeFloat (order, 'remains'); const filled = amount - remaining; let orderAmount = undefined; let cost = undefined; let average = undefined; let type = undefined; if (!price) { type = 'market'; orderAmount = baseAmount; cost = quoteAmount; average = orderAmount / cost; } else { const ta = this.safeFloat (order, 'ta:' + quoteId, 0); const tta = this.safeFloat (order, 'tta:' + quoteId, 0); const fa = this.safeFloat (order, 'fa:' + quoteId, 0); const tfa = this.safeFloat (order, 'tfa:' + quoteId, 0); if (side === 'sell') { cost = this.sum (this.sum (ta, tta), this.sum (fa, tfa)); } else { cost = this.sum (ta, tta) - this.sum (fa, tfa); } type = 'limit'; orderAmount = amount; average = cost / filled; } const time = this.safeString (order, 'time'); const lastTxTime = this.safeString (order, 'lastTxTime'); const timestamp = this.parse8601 (time); results.push ({ 'id': this.safeString (order, 'id'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastUpdated': this.parse8601 (lastTxTime), 'status': status, 'symbol': symbol, 'side': side, 'price': price, 'amount': orderAmount, 'average': average, 'type': type, 'filled': filled, 'cost': cost, 'remaining': remaining, 'fee': { 'cost': fee, 'currency': quote, }, 'info': order, }); } return results; } parseOrderStatus (status) { return this.safeString (this.options['order']['status'], status, status); } async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { if (amount === undefined) { throw new ArgumentsRequired (this.id + ' editOrder requires a amount argument'); } if (price === undefined) { throw new ArgumentsRequired (this.id + ' editOrder requires a price argument'); } await this.loadMarkets (); const market = this.market (symbol); // see: https://cex.io/rest-api#/definitions/CancelReplaceOrderRequest const request = { 'pair': market['id'], 'type': side, 'amount': amount, 'price': price, 'order_id': id, }; const response = await this.privatePostCancelReplaceOrderPair (this.extend (request, params)); return this.parseOrder (response, market); } async fetchDepositAddress (code, params = {}) { if (code === 'XRP' || code === 'XLM') { // https://github.com/ccxt/ccxt/pull/2327#issuecomment-375204856 throw new NotSupported (this.id + ' fetchDepositAddress does not support XRP and XLM addresses yet (awaiting docs from CEX.io)'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privatePostGetAddress (this.extend (request, params)); const address = this.safeString (response, 'data'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': undefined, 'info': response, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const auth = nonce + this.uid + this.apiKey; const signature = this.hmac (this.encode (auth), this.encode (this.secret)); body = this.json (this.extend ({ 'key': this.apiKey, 'signature': signature.toUpperCase (), 'nonce': nonce, }, query)); headers = { 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (Array.isArray (response)) { return response; // public endpoints may return []-arrays } if (body === 'true') { return; } if (response === undefined) { throw new NullResponse (this.id + ' returned ' + this.json (response)); } if ('e' in response) { if ('ok' in response) { if (response['ok'] === 'ok') { return; } } } if ('error' in response) { const message = this.safeString (response, 'error'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9}],59:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const foxbit = require ('./foxbit.js'); // --------------------------------------------------------------------------- module.exports = class chilebit extends foxbit { describe () { return this.deepExtend (super.describe (), { 'id': 'chilebit', 'name': 'ChileBit', 'countries': [ 'CL' ], 'has': { 'CORS': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27991414-1298f0d8-647f-11e7-9c40-d56409266336.jpg', 'api': { 'public': 'https://api.blinktrade.com/api', 'private': 'https://api.blinktrade.com/tapi', }, 'www': 'https://chilebit.net', 'doc': 'https://blinktrade.com/docs', }, 'options': { 'brokerId': '9', // https://blinktrade.com/docs/#brokers }, }); } }; },{"./foxbit.js":84}],60:[function(require,module,exports){ 'use strict'; // ---------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, AuthenticationError, RateLimitExceeded } = require ('./base/errors'); // ---------------------------------------------------------------------------- module.exports = class coinbase extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinbase', 'name': 'Coinbase', 'countries': [ 'US' ], 'rateLimit': 400, // 10k calls per hour 'version': 'v2', 'userAgent': this.userAgents['chrome'], 'headers': { 'CB-VERSION': '2018-05-30', }, 'has': { 'CORS': true, 'cancelOrder': false, 'createDepositAddress': true, 'createOrder': false, 'deposit': false, 'fetchBalance': true, 'fetchClosedOrders': false, 'fetchCurrencies': true, 'fetchDepositAddress': false, 'fetchMarkets': true, 'fetchMyTrades': false, 'fetchOHLCV': false, 'fetchOpenOrders': false, 'fetchOrder': false, 'fetchOrderBook': false, 'fetchL2OrderBook': false, 'fetchLedger': true, 'fetchOrders': false, 'fetchTicker': true, 'fetchTickers': false, 'fetchTime': true, 'fetchBidsAsks': false, 'fetchTrades': false, 'withdraw': false, 'fetchTransactions': false, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchMySells': true, 'fetchMyBuys': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/40811661-b6eceae2-653a-11e8-829e-10bfadb078cf.jpg', 'api': 'https://api.coinbase.com', 'www': 'https://www.coinbase.com', 'doc': 'https://developers.coinbase.com/api/v2', 'fees': 'https://support.coinbase.com/customer/portal/articles/2109597-buy-sell-bank-transfer-fees', 'referral': 'https://www.coinbase.com/join/58cbe25a355148797479dbd2', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'api': { 'public': { 'get': [ 'currencies', 'time', 'exchange-rates', 'users/{user_id}', 'prices/{symbol}/buy', 'prices/{symbol}/sell', 'prices/{symbol}/spot', ], }, 'private': { 'get': [ 'accounts', 'accounts/{account_id}', 'accounts/{account_id}/addresses', 'accounts/{account_id}/addresses/{address_id}', 'accounts/{account_id}/addresses/{address_id}/transactions', 'accounts/{account_id}/transactions', 'accounts/{account_id}/transactions/{transaction_id}', 'accounts/{account_id}/buys', 'accounts/{account_id}/buys/{buy_id}', 'accounts/{account_id}/sells', 'accounts/{account_id}/sells/{sell_id}', 'accounts/{account_id}/deposits', 'accounts/{account_id}/deposits/{deposit_id}', 'accounts/{account_id}/withdrawals', 'accounts/{account_id}/withdrawals/{withdrawal_id}', 'payment-methods', 'payment-methods/{payment_method_id}', 'user', 'user/auth', ], 'post': [ 'accounts', 'accounts/{account_id}/primary', 'accounts/{account_id}/addresses', 'accounts/{account_id}/transactions', 'accounts/{account_id}/transactions/{transaction_id}/complete', 'accounts/{account_id}/transactions/{transaction_id}/resend', 'accounts/{account_id}/buys', 'accounts/{account_id}/buys/{buy_id}/commit', 'accounts/{account_id}/sells', 'accounts/{account_id}/sells/{sell_id}/commit', 'accounts/{account_id}/deposists', 'accounts/{account_id}/deposists/{deposit_id}/commit', 'accounts/{account_id}/withdrawals', 'accounts/{account_id}/withdrawals/{withdrawal_id}/commit', ], 'put': [ 'accounts/{account_id}', 'user', ], 'delete': [ 'accounts/{id}', 'accounts/{account_id}/transactions/{transaction_id}', ], }, }, 'exceptions': { 'two_factor_required': AuthenticationError, // 402 When sending money over 2fa limit 'param_required': ExchangeError, // 400 Missing parameter 'validation_error': ExchangeError, // 400 Unable to validate POST/PUT 'invalid_request': ExchangeError, // 400 Invalid request 'personal_details_required': AuthenticationError, // 400 User’s personal detail required to complete this request 'identity_verification_required': AuthenticationError, // 400 Identity verification is required to complete this request 'jumio_verification_required': AuthenticationError, // 400 Document verification is required to complete this request 'jumio_face_match_verification_required': AuthenticationError, // 400 Document verification including face match is required to complete this request 'unverified_email': AuthenticationError, // 400 User has not verified their email 'authentication_error': AuthenticationError, // 401 Invalid auth (generic) 'invalid_token': AuthenticationError, // 401 Invalid Oauth token 'revoked_token': AuthenticationError, // 401 Revoked Oauth token 'expired_token': AuthenticationError, // 401 Expired Oauth token 'invalid_scope': AuthenticationError, // 403 User hasn’t authenticated necessary scope 'not_found': ExchangeError, // 404 Resource not found 'rate_limit_exceeded': RateLimitExceeded, // 429 Rate limit exceeded 'internal_server_error': ExchangeError, // 500 Internal server error }, 'options': { 'fetchCurrencies': { 'expires': 5000, }, 'accounts': [ 'wallet', 'fiat', // 'vault', ], }, }); } async fetchTime (params = {}) { const response = await this.publicGetTime (params); const data = this.safeValue (response, 'data', {}); return this.parse8601 (this.safeString (data, 'iso')); } async fetchAccounts (params = {}) { const response = await this.privateGetAccounts (params); // // { // "id": "XLM", // "name": "XLM Wallet", // "primary": false, // "type": "wallet", // "currency": { // "code": "XLM", // "name": "Stellar Lumens", // "color": "#000000", // "sort_index": 127, // "exponent": 7, // "type": "crypto", // "address_regex": "^G[A-Z2-7]{55}$", // "asset_id": "13b83335-5ede-595b-821e-5bcdfa80560f", // "destination_tag_name": "XLM Memo ID", // "destination_tag_regex": "^[ -~]{1,28}$" // }, // "balance": { // "amount": "0.0000000", // "currency": "XLM" // }, // "created_at": null, // "updated_at": null, // "resource": "account", // "resource_path": "/v2/accounts/XLM", // "allow_deposits": true, // "allow_withdrawals": true // } // const data = this.safeValue (response, 'data', []); const result = []; for (let i = 0; i < data.length; i++) { const account = data[i]; const currency = this.safeValue (account, 'currency', {}); const currencyId = this.safeString (currency, 'code'); const code = this.safeCurrencyCode (currencyId); result.push ({ 'id': this.safeString (account, 'id'), 'type': this.safeString (account, 'type'), 'code': code, 'info': account, }); } return result; } async createDepositAddress (code, params = {}) { let accountId = this.safeString (params, 'account_id'); params = this.omit (params, 'account_id'); if (accountId === undefined) { await this.loadAccounts (); for (let i = 0; i < this.accounts.length; i++) { const account = this.accounts[i]; if (account['code'] === code && account['type'] === 'wallet') { accountId = account['id']; break; } } } if (accountId === undefined) { throw new ExchangeError (this.id + ' createDepositAddress could not find the account with matching currency code, specify an `account_id` extra param'); } const request = { 'account_id': accountId, }; const response = await this.privatePostAccountsAccountIdAddresses (this.extend (request, params)); // // { // "data": { // "id": "05b1ebbf-9438-5dd4-b297-2ddedc98d0e4", // "address": "coinbasebase", // "address_info": { // "address": "coinbasebase", // "destination_tag": "287594668" // }, // "name": null, // "created_at": "2019-07-01T14:39:29Z", // "updated_at": "2019-07-01T14:39:29Z", // "network": "eosio", // "uri_scheme": "eosio", // "resource": "address", // "resource_path": "/v2/accounts/14cfc769-e852-52f3-b831-711c104d194c/addresses/05b1ebbf-9438-5dd4-b297-2ddedc98d0e4", // "warnings": [ // { // "title": "Only send EOS (EOS) to this address", // "details": "Sending any other cryptocurrency will result in permanent loss.", // "image_url": "https://dynamic-assets.coinbase.com/deaca3d47b10ed4a91a872e9618706eec34081127762d88f2476ac8e99ada4b48525a9565cf2206d18c04053f278f693434af4d4629ca084a9d01b7a286a7e26/asset_icons/1f8489bb280fb0a0fd643c1161312ba49655040e9aaaced5f9ad3eeaf868eadc.png" // }, // { // "title": "Both an address and EOS memo are required to receive EOS", // "details": "If you send funds without an EOS memo or with an incorrect EOS memo, your funds cannot be credited to your account.", // "image_url": "https://www.coinbase.com/assets/receive-warning-2f3269d83547a7748fb39d6e0c1c393aee26669bfea6b9f12718094a1abff155.png" // } // ], // "warning_title": "Only send EOS (EOS) to this address", // "warning_details": "Sending any other cryptocurrency will result in permanent loss.", // "destination_tag": "287594668", // "deposit_uri": "eosio:coinbasebase?dt=287594668", // "callback_url": null // } // } // const data = this.safeValue (response, 'data', {}); const tag = this.safeString (data, 'destination_tag'); const address = this.safeString (data, 'address'); return { 'currency': code, 'tag': tag, 'address': address, 'info': response, }; } async fetchMySells (symbol = undefined, since = undefined, limit = undefined, params = {}) { // they don't have an endpoint for all historical trades const request = await this.prepareAccountRequest (limit, params); await this.loadMarkets (); const query = this.omit (params, [ 'account_id', 'accountId' ]); const sells = await this.privateGetAccountsAccountIdSells (this.extend (request, query)); return this.parseTrades (sells['data'], undefined, since, limit); } async fetchMyBuys (symbol = undefined, since = undefined, limit = undefined, params = {}) { // they don't have an endpoint for all historical trades const request = await this.prepareAccountRequest (limit, params); await this.loadMarkets (); const query = this.omit (params, [ 'account_id', 'accountId' ]); const buys = await this.privateGetAccountsAccountIdBuys (this.extend (request, query)); return this.parseTrades (buys['data'], undefined, since, limit); } async fetchTransactionsWithMethod (method, code = undefined, since = undefined, limit = undefined, params = {}) { const request = await this.prepareAccountRequestWithCurrencyCode (code, limit, params); await this.loadMarkets (); const query = this.omit (params, [ 'account_id', 'accountId' ]); const response = await this[method] (this.extend (request, query)); return this.parseTransactions (response['data'], undefined, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { // fiat only, for crypto transactions use fetchLedger return await this.fetchTransactionsWithMethod ('privateGetAccountsAccountIdWithdrawals', code, since, limit, params); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { // fiat only, for crypto transactions use fetchLedger return await this.fetchTransactionsWithMethod ('privateGetAccountsAccountIdDeposits', code, since, limit, params); } parseTransactionStatus (status) { const statuses = { 'created': 'pending', 'completed': 'ok', 'canceled': 'canceled', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, market = undefined) { // // fiat deposit // // { // "id": "f34c19f3-b730-5e3d-9f72", // "status": "completed", // "payment_method": { // "id": "a022b31d-f9c7-5043-98f2", // "resource": "payment_method", // "resource_path": "/v2/payment-methods/a022b31d-f9c7-5043-98f2" // }, // "transaction": { // "id": "04ed4113-3732-5b0c-af86-b1d2146977d0", // "resource": "transaction", // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/04ed4113-3732-5b0c-af86" // }, // "user_reference": "2VTYTH", // "created_at": "2017-02-09T07:01:18Z", // "updated_at": "2017-02-09T07:01:26Z", // "resource": "deposit", // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/deposits/f34c19f3-b730-5e3d-9f72", // "committed": true, // "payout_at": "2017-02-12T07:01:17Z", // "instant": false, // "fee": { "amount": "0.00", "currency": "EUR" }, // "amount": { "amount": "114.02", "currency": "EUR" }, // "subtotal": { "amount": "114.02", "currency": "EUR" }, // "hold_until": null, // "hold_days": 0, // "hold_business_days": 0, // "next_step": null // } // // fiat_withdrawal // // { // "id": "cfcc3b4a-eeb6-5e8c-8058", // "status": "completed", // "payment_method": { // "id": "8b94cfa4-f7fd-5a12-a76a", // "resource": "payment_method", // "resource_path": "/v2/payment-methods/8b94cfa4-f7fd-5a12-a76a" // }, // "transaction": { // "id": "fcc2550b-5104-5f83-a444", // "resource": "transaction", // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/fcc2550b-5104-5f83-a444" // }, // "user_reference": "MEUGK", // "created_at": "2018-07-26T08:55:12Z", // "updated_at": "2018-07-26T08:58:18Z", // "resource": "withdrawal", // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/withdrawals/cfcc3b4a-eeb6-5e8c-8058", // "committed": true, // "payout_at": "2018-07-31T08:55:12Z", // "instant": false, // "fee": { "amount": "0.15", "currency": "EUR" }, // "amount": { "amount": "13130.69", "currency": "EUR" }, // "subtotal": { "amount": "13130.84", "currency": "EUR" }, // "idem": "e549dee5-63ed-4e79-8a96", // "next_step": null // } // const subtotalObject = this.safeValue (transaction, 'subtotal', {}); const feeObject = this.safeValue (transaction, 'fee', {}); const id = this.safeString (transaction, 'id'); const timestamp = this.parse8601 (this.safeValue (transaction, 'created_at')); const updated = this.parse8601 (this.safeValue (transaction, 'updated_at')); const type = this.safeString (transaction, 'resource'); const amount = this.safeFloat (subtotalObject, 'amount'); const currencyId = this.safeString (subtotalObject, 'currency'); const currency = this.safeCurrencyCode (currencyId); const feeCost = this.safeFloat (feeObject, 'amount'); const feeCurrencyId = this.safeString (feeObject, 'currency'); const feeCurrency = this.safeCurrencyCode (feeCurrencyId); const fee = { 'cost': feeCost, 'currency': feeCurrency, }; let status = this.parseTransactionStatus (this.safeString (transaction, 'status')); if (status === undefined) { const committed = this.safeValue (transaction, 'committed'); status = committed ? 'ok' : 'pending'; } return { 'info': transaction, 'id': id, 'txid': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': undefined, 'tag': undefined, 'type': type, 'amount': amount, 'currency': currency, 'status': status, 'updated': updated, 'fee': fee, }; } parseTrade (trade, market = undefined) { // // { // "id": "67e0eaec-07d7-54c4-a72c-2e92826897df", // "status": "completed", // "payment_method": { // "id": "83562370-3e5c-51db-87da-752af5ab9559", // "resource": "payment_method", // "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" // }, // "transaction": { // "id": "441b9494-b3f0-5b98-b9b0-4d82c21c252a", // "resource": "transaction", // "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" // }, // "amount": { "amount": "1.00000000", "currency": "BTC" }, // "total": { "amount": "10.25", "currency": "USD" }, // "subtotal": { "amount": "10.10", "currency": "USD" }, // "created_at": "2015-01-31T20:49:02Z", // "updated_at": "2015-02-11T16:54:02-08:00", // "resource": "buy", // "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/67e0eaec-07d7-54c4-a72c-2e92826897df", // "committed": true, // "instant": false, // "fee": { "amount": "0.15", "currency": "USD" }, // "payout_at": "2015-02-18T16:54:00-08:00" // } // let symbol = undefined; const totalObject = this.safeValue (trade, 'total', {}); const amountObject = this.safeValue (trade, 'amount', {}); const subtotalObject = this.safeValue (trade, 'subtotal', {}); const feeObject = this.safeValue (trade, 'fee', {}); const id = this.safeString (trade, 'id'); const timestamp = this.parse8601 (this.safeValue (trade, 'created_at')); if (market === undefined) { const baseId = this.safeString (totalObject, 'currency'); const quoteId = this.safeString (amountObject, 'currency'); if ((baseId !== undefined) && (quoteId !== undefined)) { const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } const orderId = undefined; const side = this.safeString (trade, 'resource'); const type = undefined; const cost = this.safeFloat (subtotalObject, 'amount'); const amount = this.safeFloat (amountObject, 'amount'); let price = undefined; if (cost !== undefined) { if (amount !== undefined) { price = cost / amount; } } const feeCost = this.safeFloat (feeObject, 'amount'); const feeCurrencyId = this.safeString (feeObject, 'currency'); const feeCurrency = this.safeCurrencyCode (feeCurrencyId); const fee = { 'cost': feeCost, 'currency': feeCurrency, }; return { 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchMarkets (params = {}) { const response = await this.fetchCurrenciesFromCache (params); const currencies = this.safeValue (response, 'currencies', {}); const exchangeRates = this.safeValue (response, 'exchangeRates', {}); const data = this.safeValue (currencies, 'data', []); const dataById = this.indexBy (data, 'id'); const rates = this.safeValue (this.safeValue (exchangeRates, 'data', {}), 'rates', {}); const baseIds = Object.keys (rates); const result = []; for (let i = 0; i < baseIds.length; i++) { const baseId = baseIds[i]; const base = this.safeCurrencyCode (baseId); const type = (baseId in dataById) ? 'fiat' : 'crypto'; // https://github.com/ccxt/ccxt/issues/6066 if (type === 'crypto') { for (let j = 0; j < data.length; j++) { const quoteCurrency = data[j]; const quoteId = this.safeString (quoteCurrency, 'id'); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const id = baseId + '-' + quoteId; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': undefined, 'info': quoteCurrency, 'precision': { 'amount': undefined, 'price': undefined, }, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeFloat (quoteCurrency, 'min_size'), 'max': undefined, }, }, }); } } } return result; } async fetchCurrenciesFromCache (params = {}) { const options = this.safeValue (this.options, 'fetchCurrencies', {}); const timestamp = this.safeInteger (options, 'timestamp'); const expires = this.safeInteger (options, 'expires', 1000); const now = this.milliseconds (); if ((timestamp === undefined) || ((now - timestamp) > expires)) { const currencies = await this.publicGetCurrencies (params); const exchangeRates = await this.publicGetExchangeRates (params); this.options['fetchCurrencies'] = this.extend (options, { 'currencies': currencies, 'exchangeRates': exchangeRates, 'timestamp': now, }); } return this.safeValue (this.options, 'fetchCurrencies', {}); } async fetchCurrencies (params = {}) { const response = await this.fetchCurrenciesFromCache (params); const currencies = this.safeValue (response, 'currencies', {}); // // { // "data":[ // {"id":"AED","name":"United Arab Emirates Dirham","min_size":"0.01000000"}, // {"id":"AFN","name":"Afghan Afghani","min_size":"0.01000000"}, // {"id":"ALL","name":"Albanian Lek","min_size":"0.01000000"}, // {"id":"AMD","name":"Armenian Dram","min_size":"0.01000000"}, // {"id":"ANG","name":"Netherlands Antillean Gulden","min_size":"0.01000000"}, // // ... // ], // } // const exchangeRates = this.safeValue (response, 'exchangeRates', {}); // // { // "data":{ // "currency":"USD", // "rates":{ // "AED":"3.67", // "AFN":"78.21", // "ALL":"110.42", // "AMD":"474.18", // "ANG":"1.75", // // ... // }, // } // } // const data = this.safeValue (currencies, 'data', []); const dataById = this.indexBy (data, 'id'); const rates = this.safeValue (this.safeValue (exchangeRates, 'data', {}), 'rates', {}); const keys = Object.keys (rates); const result = {}; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const type = (key in dataById) ? 'fiat' : 'crypto'; const currency = this.safeValue (dataById, key, {}); const id = this.safeString (currency, 'id', key); const name = this.safeString (currency, 'name'); const code = this.safeCurrencyCode (id); result[code] = { 'id': id, 'code': code, 'info': currency, // the original payload 'type': type, 'name': name, 'active': true, 'fee': undefined, 'precision': undefined, 'limits': { 'amount': { 'min': this.safeFloat (currency, 'min_size'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, }; } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const timestamp = this.seconds (); const market = this.market (symbol); const request = this.extend ({ 'symbol': market['id'], }, params); const buy = await this.publicGetPricesSymbolBuy (request); const sell = await this.publicGetPricesSymbolSell (request); const spot = await this.publicGetPricesSymbolSpot (request); const ask = this.safeFloat (buy['data'], 'amount'); const bid = this.safeFloat (sell['data'], 'amount'); const last = this.safeFloat (spot['data'], 'amount'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'bid': bid, 'ask': ask, 'last': last, 'high': undefined, 'low': undefined, 'bidVolume': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': undefined, 'info': { 'buy': buy, 'sell': sell, 'spot': spot, }, }; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccounts (params); const balances = this.safeValue (response, 'data'); const accounts = this.safeValue (params, 'type', this.options['accounts']); const result = { 'info': response }; for (let b = 0; b < balances.length; b++) { const balance = balances[b]; if (this.inArray (balance['type'], accounts)) { const currencyId = this.safeString (balance['balance'], 'currency'); const code = this.safeCurrencyCode (currencyId); const total = this.safeFloat (balance['balance'], 'amount'); const free = total; const used = undefined; if (code in result) { result[code]['free'] = this.sum (result[code]['free'], total); result[code]['total'] = this.sum (result[code]['total'], total); } else { const account = { 'free': free, 'used': used, 'total': total, }; result[code] = account; } } } return this.parseBalance (result); } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = await this.prepareAccountRequestWithCurrencyCode (code, limit, params); const query = this.omit (params, ['account_id', 'accountId']); // for pagination use parameter 'starting_after' // the value for the next page can be obtained from the result of the previous call in the 'pagination' field // eg: instance.last_json_response.pagination.next_starting_after const response = await this.privateGetAccountsAccountIdTransactions (this.extend (request, query)); return this.parseLedger (response['data'], undefined, since, limit); } parseLedgerEntryStatus (status) { const types = { 'completed': 'ok', }; return this.safeString (types, status, status); } parseLedgerEntryType (type) { const types = { 'buy': 'trade', 'sell': 'trade', 'fiat_deposit': 'transaction', 'fiat_withdrawal': 'transaction', 'exchange_deposit': 'transaction', // fiat withdrawal (from coinbase to coinbasepro) 'exchange_withdrawal': 'transaction', // fiat deposit (to coinbase from coinbasepro) 'send': 'transaction', // crypto deposit OR withdrawal 'pro_deposit': 'transaction', // crypto withdrawal (from coinbase to coinbasepro) 'pro_withdrawal': 'transaction', // crypto deposit (to coinbase from coinbasepro) }; return this.safeString (types, type, type); } parseLedgerEntry (item, currency = undefined) { // // crypto deposit transaction // // { // id: '34e4816b-4c8c-5323-a01c-35a9fa26e490', // type: 'send', // status: 'completed', // amount: { amount: '28.31976528', currency: 'BCH' }, // native_amount: { amount: '2799.65', currency: 'GBP' }, // description: null, // created_at: '2019-02-28T12:35:20Z', // updated_at: '2019-02-28T12:43:24Z', // resource: 'transaction', // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/34e4816b-4c8c-5323-a01c-35a9fa26e490', // instant_exchange: false, // network: { // status: 'confirmed', // hash: '56222d865dae83774fccb2efbd9829cf08c75c94ce135bfe4276f3fb46d49701', // transaction_url: 'https://bch.btc.com/56222d865dae83774fccb2efbd9829cf08c75c94ce135bfe4276f3fb46d49701' // }, // from: { resource: 'bitcoin_cash_network', currency: 'BCH' }, // details: { title: 'Received Bitcoin Cash', subtitle: 'From Bitcoin Cash address' } // } // // crypto withdrawal transaction // // { // id: '459aad99-2c41-5698-ac71-b6b81a05196c', // type: 'send', // status: 'completed', // amount: { amount: '-0.36775642', currency: 'BTC' }, // native_amount: { amount: '-1111.65', currency: 'GBP' }, // description: null, // created_at: '2019-03-20T08:37:07Z', // updated_at: '2019-03-20T08:49:33Z', // resource: 'transaction', // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/459aad99-2c41-5698-ac71-b6b81a05196c', // instant_exchange: false, // network: { // status: 'confirmed', // hash: '2732bbcf35c69217c47b36dce64933d103895277fe25738ffb9284092701e05b', // transaction_url: 'https://blockchain.info/tx/2732bbcf35c69217c47b36dce64933d103895277fe25738ffb9284092701e05b', // transaction_fee: { amount: '0.00000000', currency: 'BTC' }, // transaction_amount: { amount: '0.36775642', currency: 'BTC' }, // confirmations: 15682 // }, // to: { // resource: 'bitcoin_address', // address: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', // currency: 'BTC', // address_info: { address: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX' } // }, // idem: 'da0a2f14-a2af-4c5a-a37e-d4484caf582bsend', // application: { // id: '5756ab6e-836b-553b-8950-5e389451225d', // resource: 'application', // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' // }, // details: { title: 'Sent Bitcoin', subtitle: 'To Bitcoin address' } // } // // withdrawal transaction from coinbase to coinbasepro // // { // id: '5b1b9fb8-5007-5393-b923-02903b973fdc', // type: 'pro_deposit', // status: 'completed', // amount: { amount: '-0.00001111', currency: 'BCH' }, // native_amount: { amount: '0.00', currency: 'GBP' }, // description: null, // created_at: '2019-02-28T13:31:58Z', // updated_at: '2019-02-28T13:31:58Z', // resource: 'transaction', // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/5b1b9fb8-5007-5393-b923-02903b973fdc', // instant_exchange: false, // application: { // id: '5756ab6e-836b-553b-8950-5e389451225d', // resource: 'application', // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' // }, // details: { title: 'Transferred Bitcoin Cash', subtitle: 'To Coinbase Pro' } // } // // withdrawal transaction from coinbase to gdax // // { // id: 'badb7313-a9d3-5c07-abd0-00f8b44199b1', // type: 'exchange_deposit', // status: 'completed', // amount: { amount: '-0.43704149', currency: 'BCH' }, // native_amount: { amount: '-51.90', currency: 'GBP' }, // description: null, // created_at: '2019-03-19T10:30:40Z', // updated_at: '2019-03-19T10:30:40Z', // resource: 'transaction', // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/badb7313-a9d3-5c07-abd0-00f8b44199b1', // instant_exchange: false, // details: { title: 'Transferred Bitcoin Cash', subtitle: 'To GDAX' } // } // // deposit transaction from gdax to coinbase // // { // id: '9c4b642c-8688-58bf-8962-13cef64097de', // type: 'exchange_withdrawal', // status: 'completed', // amount: { amount: '0.57729420', currency: 'BTC' }, // native_amount: { amount: '4418.72', currency: 'GBP' }, // description: null, // created_at: '2018-02-17T11:33:33Z', // updated_at: '2018-02-17T11:33:33Z', // resource: 'transaction', // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/9c4b642c-8688-58bf-8962-13cef64097de', // instant_exchange: false, // details: { title: 'Transferred Bitcoin', subtitle: 'From GDAX' } // } // // deposit transaction from coinbasepro to coinbase // // { // id: '8d6dd0b9-3416-568a-889d-8f112fae9e81', // type: 'pro_withdrawal', // status: 'completed', // amount: { amount: '0.40555386', currency: 'BTC' }, // native_amount: { amount: '1140.27', currency: 'GBP' }, // description: null, // created_at: '2019-03-04T19:41:58Z', // updated_at: '2019-03-04T19:41:58Z', // resource: 'transaction', // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/8d6dd0b9-3416-568a-889d-8f112fae9e81', // instant_exchange: false, // application: { // id: '5756ab6e-836b-553b-8950-5e389451225d', // resource: 'application', // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' // }, // details: { title: 'Transferred Bitcoin', subtitle: 'From Coinbase Pro' } // } // // sell trade // // { // id: 'a9409207-df64-585b-97ab-a50780d2149e', // type: 'sell', // status: 'completed', // amount: { amount: '-9.09922880', currency: 'BTC' }, // native_amount: { amount: '-7285.73', currency: 'GBP' }, // description: null, // created_at: '2017-03-27T15:38:34Z', // updated_at: '2017-03-27T15:38:34Z', // resource: 'transaction', // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/a9409207-df64-585b-97ab-a50780d2149e', // instant_exchange: false, // sell: { // id: 'e3550b4d-8ae6-5de3-95fe-1fb01ba83051', // resource: 'sell', // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/sells/e3550b4d-8ae6-5de3-95fe-1fb01ba83051' // }, // details: { // title: 'Sold Bitcoin', // subtitle: 'Using EUR Wallet', // payment_method_name: 'EUR Wallet' // } // } // // buy trade // // { // id: '63eeed67-9396-5912-86e9-73c4f10fe147', // type: 'buy', // status: 'completed', // amount: { amount: '2.39605772', currency: 'ETH' }, // native_amount: { amount: '98.31', currency: 'GBP' }, // description: null, // created_at: '2017-03-27T09:07:56Z', // updated_at: '2017-03-27T09:07:57Z', // resource: 'transaction', // resource_path: '/v2/accounts/8902f85d-4a69-5d74-82fe-8e390201bda7/transactions/63eeed67-9396-5912-86e9-73c4f10fe147', // instant_exchange: false, // buy: { // id: '20b25b36-76c6-5353-aa57-b06a29a39d82', // resource: 'buy', // resource_path: '/v2/accounts/8902f85d-4a69-5d74-82fe-8e390201bda7/buys/20b25b36-76c6-5353-aa57-b06a29a39d82' // }, // details: { // title: 'Bought Ethereum', // subtitle: 'Using EUR Wallet', // payment_method_name: 'EUR Wallet' // } // } // // fiat deposit transaction // // { // id: '04ed4113-3732-5b0c-af86-b1d2146977d0', // type: 'fiat_deposit', // status: 'completed', // amount: { amount: '114.02', currency: 'EUR' }, // native_amount: { amount: '97.23', currency: 'GBP' }, // description: null, // created_at: '2017-02-09T07:01:21Z', // updated_at: '2017-02-09T07:01:22Z', // resource: 'transaction', // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/04ed4113-3732-5b0c-af86-b1d2146977d0', // instant_exchange: false, // fiat_deposit: { // id: 'f34c19f3-b730-5e3d-9f72-96520448677a', // resource: 'fiat_deposit', // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/deposits/f34c19f3-b730-5e3d-9f72-96520448677a' // }, // details: { // title: 'Deposited funds', // subtitle: 'From SEPA Transfer (GB47 BARC 20..., reference CBADVI)', // payment_method_name: 'SEPA Transfer (GB47 BARC 20..., reference CBADVI)' // } // } // // fiat withdrawal transaction // // { // id: '957d98e2-f80e-5e2f-a28e-02945aa93079', // type: 'fiat_withdrawal', // status: 'completed', // amount: { amount: '-11000.00', currency: 'EUR' }, // native_amount: { amount: '-9698.22', currency: 'GBP' }, // description: null, // created_at: '2017-12-06T13:19:19Z', // updated_at: '2017-12-06T13:19:19Z', // resource: 'transaction', // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/957d98e2-f80e-5e2f-a28e-02945aa93079', // instant_exchange: false, // fiat_withdrawal: { // id: 'f4bf1fd9-ab3b-5de7-906d-ed3e23f7a4e7', // resource: 'fiat_withdrawal', // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/withdrawals/f4bf1fd9-ab3b-5de7-906d-ed3e23f7a4e7' // }, // details: { // title: 'Withdrew funds', // subtitle: 'To HSBC BANK PLC (GB74 MIDL...)', // payment_method_name: 'HSBC BANK PLC (GB74 MIDL...)' // } // } // const amountInfo = this.safeValue (item, 'amount', {}); let amount = this.safeFloat (amountInfo, 'amount'); let direction = undefined; if (amount < 0) { direction = 'out'; amount = -amount; } else { direction = 'in'; } const currencyId = this.safeString (amountInfo, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); // // the address and txid do not belong to the unified ledger structure // // let address = undefined; // if (item['to']) { // address = this.safeString (item['to'], 'address'); // } // let txid = undefined; // let fee = undefined; const networkInfo = this.safeValue (item, 'network', {}); // txid = network['hash']; // txid does not belong to the unified ledger structure const feeInfo = this.safeValue (networkInfo, 'transaction_fee'); if (feeInfo !== undefined) { const feeCurrencyId = this.safeString (feeInfo, 'currency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId, currency); const feeAmount = this.safeFloat (feeInfo, 'amount'); fee = { 'cost': feeAmount, 'currency': feeCurrencyCode, }; } const timestamp = this.parse8601 (this.safeValue (item, 'created_at')); const id = this.safeString (item, 'id'); const type = this.parseLedgerEntryType (this.safeString (item, 'type')); const status = this.parseLedgerEntryStatus (this.safeString (item, 'status')); const path = this.safeString (item, 'resource_path'); let accountId = undefined; if (path !== undefined) { const parts = path.split ('/'); const numParts = parts.length; if (numParts > 3) { accountId = parts[3]; } } return { 'info': item, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'direction': direction, 'account': accountId, 'referenceId': undefined, 'referenceAccount': undefined, 'type': type, 'currency': code, 'amount': amount, 'before': undefined, 'after': undefined, 'status': status, 'fee': fee, }; } async findAccountId (code) { await this.loadMarkets (); await this.loadAccounts (); for (let i = 0; i < this.accounts.length; i++) { const account = this.accounts[i]; if (account['code'] === code) { return account['id']; } } return undefined; } prepareAccountRequest (limit = undefined, params = {}) { const accountId = this.safeString2 (params, 'account_id', 'accountId'); if (accountId === undefined) { throw new ArgumentsRequired (this.id + ' method requires an account_id (or accountId) parameter'); } const request = { 'account_id': accountId, }; if (limit !== undefined) { request['limit'] = limit; } return request; } async prepareAccountRequestWithCurrencyCode (code = undefined, limit = undefined, params = {}) { let accountId = this.safeString2 (params, 'account_id', 'accountId'); if (accountId === undefined) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' method requires an account_id (or accountId) parameter OR a currency code argument'); } accountId = await this.findAccountId (code); if (accountId === undefined) { throw new ExchangeError (this.id + ' could not find account id for ' + code); } } const request = { 'account_id': accountId, }; if (limit !== undefined) { request['limit'] = limit; } return request; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let fullPath = '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (method === 'GET') { if (Object.keys (query).length) { fullPath += '?' + this.urlencode (query); } } const url = this.urls['api'] + fullPath; if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); let payload = ''; if (method !== 'GET') { if (Object.keys (query).length) { body = this.json (query); payload = body; } } const auth = nonce + method + fullPath + payload; const signature = this.hmac (this.encode (auth), this.encode (this.secret)); headers = { 'CB-ACCESS-KEY': this.apiKey, 'CB-ACCESS-SIGN': signature, 'CB-ACCESS-TIMESTAMP': nonce, 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } const feedback = this.id + ' ' + body; // // {"error": "invalid_request", "error_description": "The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed."} // // or // // { // "errors": [ // { // "id": "not_found", // "message": "Not found" // } // ] // } // let errorCode = this.safeString (response, 'error'); if (errorCode !== undefined) { this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); throw new ExchangeError (feedback); } const errors = this.safeValue (response, 'errors'); if (errors !== undefined) { if (Array.isArray (errors)) { const numErrors = errors.length; if (numErrors > 0) { errorCode = this.safeString (errors[0], 'id'); if (errorCode !== undefined) { this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); throw new ExchangeError (feedback); } } } } const data = this.safeValue (response, 'data'); if (data === undefined) { throw new ExchangeError (this.id + ' failed due to a malformed response ' + this.json (response)); } } }; },{"./base/Exchange":7,"./base/errors":9}],61:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const coinbasepro = require ('./coinbasepro.js'); // --------------------------------------------------------------------------- module.exports = class coinbaseprime extends coinbasepro { describe () { return this.deepExtend (super.describe (), { 'id': 'coinbaseprime', 'name': 'Coinbase Prime', 'pro': true, 'urls': { 'test': { 'public': 'https://api-public.sandbox.prime.coinbase.com', 'private': 'https://api-public.sandbox.prime.coinbase.com', }, 'logo': 'https://user-images.githubusercontent.com/1294454/44539184-29f26e00-a70c-11e8-868f-e907fc236a7c.jpg', 'api': { 'public': 'https://api.prime.coinbase.com', 'private': 'https://api.prime.coinbase.com', }, 'www': 'https://prime.coinbase.com', 'doc': 'https://docs.prime.coinbase.com', 'fees': 'https://support.prime.coinbase.com/customer/en/portal/articles/2945629-fees?b_id=17475', }, }); } }; },{"./coinbasepro.js":62}],62:[function(require,module,exports){ 'use strict'; // ---------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { InsufficientFunds, ArgumentsRequired, ExchangeError, InvalidOrder, InvalidAddress, AuthenticationError, NotSupported, OrderNotFound, OnMaintenance, RateLimitExceeded } = require ('./base/errors'); // ---------------------------------------------------------------------------- module.exports = class coinbasepro extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinbasepro', 'name': 'Coinbase Pro', 'countries': [ 'US' ], 'rateLimit': 1000, 'userAgent': this.userAgents['chrome'], 'pro': true, 'has': { 'cancelAllOrders': true, 'CORS': true, 'deposit': true, 'fetchAccounts': true, 'fetchClosedOrders': true, 'fetchDepositAddress': true, 'createDepositAddress': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderTrades': true, 'fetchOrders': true, 'fetchTime': true, 'fetchTransactions': true, 'withdraw': true, }, 'timeframes': { '1m': 60, '5m': 300, '15m': 900, '1h': 3600, '6h': 21600, '1d': 86400, }, 'urls': { 'test': { 'public': 'https://api-public.sandbox.pro.coinbase.com', 'private': 'https://api-public.sandbox.pro.coinbase.com', }, 'logo': 'https://user-images.githubusercontent.com/1294454/41764625-63b7ffde-760a-11e8-996d-a6328fa9347a.jpg', 'api': { 'public': 'https://api.pro.coinbase.com', 'private': 'https://api.pro.coinbase.com', }, 'www': 'https://pro.coinbase.com/', 'doc': 'https://docs.pro.coinbase.com', 'fees': [ 'https://docs.pro.coinbase.com/#fees', 'https://support.pro.coinbase.com/customer/en/portal/articles/2945310-fees', ], }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'password': true, }, 'api': { 'public': { 'get': [ 'currencies', 'products', 'products/{id}/book', 'products/{id}/candles', 'products/{id}/stats', 'products/{id}/ticker', 'products/{id}/trades', 'time', ], }, 'private': { 'get': [ 'accounts', 'accounts/{id}', 'accounts/{id}/holds', 'accounts/{id}/ledger', 'accounts/{id}/transfers', 'coinbase-accounts', 'coinbase-accounts/{id}/addresses', 'fills', 'funding', 'fees', 'orders', 'orders/{id}', 'otc/orders', 'payment-methods', 'position', 'reports/{id}', 'users/self/trailing-volume', ], 'post': [ 'conversions', 'deposits/coinbase-account', 'deposits/payment-method', 'coinbase-accounts/{id}/addresses', 'funding/repay', 'orders', 'position/close', 'profiles/margin-transfer', 'reports', 'withdrawals/coinbase', 'withdrawals/crypto', 'withdrawals/payment-method', ], 'delete': [ 'orders', 'orders/{id}', ], }, }, 'fees': { 'trading': { 'tierBased': true, // complicated tier system per coin 'percentage': true, 'maker': 0.5 / 100, // highest fee of all tiers 'taker': 0.5 / 100, // highest fee of all tiers }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BCH': 0, 'BTC': 0, 'LTC': 0, 'ETH': 0, 'EUR': 0.15, 'USD': 25, }, 'deposit': { 'BCH': 0, 'BTC': 0, 'LTC': 0, 'ETH': 0, 'EUR': 0.15, 'USD': 10, }, }, }, 'exceptions': { 'exact': { 'Insufficient funds': InsufficientFunds, 'NotFound': OrderNotFound, 'Invalid API Key': AuthenticationError, 'invalid signature': AuthenticationError, 'Invalid Passphrase': AuthenticationError, 'Invalid order id': InvalidOrder, 'Private rate limit exceeded': RateLimitExceeded, }, 'broad': { 'Order already done': OrderNotFound, 'order not found': OrderNotFound, 'price too small': InvalidOrder, 'price too precise': InvalidOrder, 'under maintenance': OnMaintenance, }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetProducts (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'id'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const priceLimits = { 'min': this.safeFloat (market, 'quote_increment'), 'max': undefined, }; const precision = { 'amount': this.precisionFromString (this.safeString (market, 'base_increment')), 'price': this.precisionFromString (this.safeString (market, 'quote_increment')), }; const active = market['status'] === 'online'; result.push (this.extend (this.fees['trading'], { 'id': id, 'symbol': symbol, 'baseId': baseId, 'quoteId': quoteId, 'base': base, 'quote': quote, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (market, 'base_min_size'), 'max': this.safeFloat (market, 'base_max_size'), }, 'price': priceLimits, 'cost': { 'min': this.safeFloat (market, 'min_market_funds'), 'max': this.safeFloat (market, 'max_market_funds'), }, }, 'active': active, 'info': market, })); } return result; } async fetchAccounts (params = {}) { const response = await this.privateGetAccounts (params); // // [ // { // id: '4aac9c60-cbda-4396-9da4-4aa71e95fba0', // currency: 'BTC', // balance: '0.0000000000000000', // available: '0', // hold: '0.0000000000000000', // profile_id: 'b709263e-f42a-4c7d-949a-a95c83d065da' // }, // { // id: 'f75fa69a-1ad1-4a80-bd61-ee7faa6135a3', // currency: 'USDC', // balance: '0.0000000000000000', // available: '0', // hold: '0.0000000000000000', // profile_id: 'b709263e-f42a-4c7d-949a-a95c83d065da' // }, // ] // const result = []; for (let i = 0; i < response.length; i++) { const account = response[i]; const accountId = this.safeString (account, 'id'); const currencyId = this.safeString (account, 'currency'); const code = this.safeCurrencyCode (currencyId); result.push ({ 'id': accountId, 'type': undefined, 'currency': code, 'info': account, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccounts (params); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = { 'free': this.safeFloat (balance, 'available'), 'used': this.safeFloat (balance, 'hold'), 'total': this.safeFloat (balance, 'balance'), }; result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); // level 1 - only the best bid and ask // level 2 - top 50 bids and asks (aggregated) // level 3 - full order book (non aggregated) const request = { 'id': this.marketId (symbol), 'level': 2, // 1 best bidask, 2 aggregated, 3 full }; const response = await this.publicGetProductsIdBook (this.extend (request, params)); // // { // "sequence":1924393896, // "bids":[ // ["0.01825","24.34811287",2], // ["0.01824","72.5463",3], // ["0.01823","424.54298049",6], // ], // "asks":[ // ["0.01826","171.10414904",4], // ["0.01827","22.60427028",1], // ["0.01828","397.46018784",7], // ] // } // const orderbook = this.parseOrderBook (response); orderbook['nonce'] = this.safeInteger (response, 'sequence'); return orderbook; } parseTicker (ticker, market = undefined) { // // publicGetProductsIdTicker // // { // "trade_id":843439, // "price":"0.997999", // "size":"80.29769", // "time":"2020-01-28T02:13:33.012523Z", // "bid":"0.997094", // "ask":"0.998", // "volume":"1903188.03750000" // } // // publicGetProductsIdStats // // { // "open": "34.19000000", // "high": "95.70000000", // "low": "7.06000000", // "volume": "2.41000000" // } // const timestamp = this.parse8601 (this.safeValue (ticker, 'time')); const bid = this.safeFloat (ticker, 'bid'); const ask = this.safeFloat (ticker, 'ask'); const last = this.safeFloat (ticker, 'price'); const symbol = (market === undefined) ? undefined : market['symbol']; return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': bid, 'bidVolume': undefined, 'ask': ask, 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; // publicGetProductsIdTicker or publicGetProductsIdStats const method = this.safeString (this.options, 'fetchTickerMethod', 'publicGetProductsIdTicker'); const response = await this[method] (this.extend (request, params)); // // publicGetProductsIdTicker // // { // "trade_id":843439, // "price":"0.997999", // "size":"80.29769", // "time":"2020-01-28T02:13:33.012523Z", // "bid":"0.997094", // "ask":"0.998", // "volume":"1903188.03750000" // } // // publicGetProductsIdStats // // { // "open": "34.19000000", // "high": "95.70000000", // "low": "7.06000000", // "volume": "2.41000000" // } // return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { // // { // type: 'match', // trade_id: 82047307, // maker_order_id: '0f358725-2134-435e-be11-753912a326e0', // taker_order_id: '252b7002-87a3-425c-ac73-f5b9e23f3caf', // side: 'sell', // size: '0.00513192', // price: '9314.78', // product_id: 'BTC-USD', // sequence: 12038915443, // time: '2020-01-31T20:03:41.158814Z' // } // const timestamp = this.parse8601 (this.safeString2 (trade, 'time', 'created_at')); let symbol = undefined; const marketId = this.safeString (trade, 'product_id'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } let feeRate = undefined; let feeCurrency = undefined; let takerOrMaker = undefined; if (market !== undefined) { feeCurrency = market['quote']; if ('liquidity' in trade) { takerOrMaker = (trade['liquidity'] === 'T') ? 'taker' : 'maker'; feeRate = market[takerOrMaker]; } } const feeCost = this.safeFloat2 (trade, 'fill_fees', 'fee'); const fee = { 'cost': feeCost, 'currency': feeCurrency, 'rate': feeRate, }; const type = undefined; const id = this.safeString (trade, 'trade_id'); let side = (trade['side'] === 'buy') ? 'sell' : 'buy'; const orderId = this.safeString (trade, 'order_id'); // Coinbase Pro returns inverted side to fetchMyTrades vs fetchTrades if (orderId !== undefined) { side = (trade['side'] === 'buy') ? 'buy' : 'sell'; } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'size'); return { 'id': id, 'order': orderId, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'fee': fee, 'cost': price * amount, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { // as of 2018-08-23 if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'product_id': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetFills (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], // fixes issue #2 }; const response = await this.publicGetProductsIdTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, ohlcv[3], ohlcv[2], ohlcv[1], ohlcv[4], ohlcv[5], ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const granularity = this.timeframes[timeframe]; const request = { 'id': market['id'], 'granularity': granularity, }; if (since !== undefined) { request['start'] = this.iso8601 (since); if (limit === undefined) { // https://docs.pro.coinbase.com/#get-historic-rates limit = 300; // max = 300 } request['end'] = this.iso8601 (this.sum ((limit - 1) * granularity * 1000, since)); } const response = await this.publicGetProductsIdCandles (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchTime (params = {}) { const response = await this.publicGetTime (params); return this.parse8601 (this.safeString (response, 'iso')); } parseOrderStatus (status) { const statuses = { 'pending': 'open', 'active': 'open', 'open': 'open', 'done': 'closed', 'canceled': 'canceled', 'canceling': 'open', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const timestamp = this.parse8601 (this.safeString (order, 'created_at')); let symbol = undefined; const marketId = this.safeString (order, 'product_id'); let quote = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } const status = this.parseOrderStatus (this.safeString (order, 'status')); const price = this.safeFloat (order, 'price'); const filled = this.safeFloat (order, 'filled_size'); const amount = this.safeFloat (order, 'size', filled); let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = amount - filled; } } const cost = this.safeFloat (order, 'executed_value'); const feeCost = this.safeFloat (order, 'fill_fees'); let fee = undefined; if (feeCost !== undefined) { let feeCurrencyCode = undefined; if (market !== undefined) { feeCurrencyCode = market['quote']; } else if (quote !== undefined) { feeCurrencyCode = quote; } fee = { 'cost': feeCost, 'currency': feeCurrencyCode, 'rate': undefined, }; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const id = this.safeString (order, 'id'); const type = this.safeString (order, 'type'); const side = this.safeString (order, 'side'); return { 'id': id, 'clientOrderId': undefined, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'fee': fee, 'average': undefined, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privateGetOrdersId (this.extend (request, params)); return this.parseOrder (response); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'order_id': id, }; const response = await this.privateGetFills (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'status': 'all', }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['product_id'] = market['id']; } const response = await this.privateGetOrders (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['product_id'] = market['id']; } const response = await this.privateGetOrders (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'status': 'done', }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['product_id'] = market['id']; } const response = await this.privateGetOrders (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); // let oid = this.nonce ().toString (); const request = { 'product_id': this.marketId (symbol), 'side': side, 'size': this.amountToPrecision (symbol, amount), 'type': type, }; if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostOrders (this.extend (request, params)); return this.parseOrder (response); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); return await this.privateDeleteOrdersId ({ 'id': id }); } async cancelAllOrders (symbol = undefined, params = {}) { return await this.privateDeleteOrders (params); } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; const rate = market[takerOrMaker]; const cost = amount * price; const currency = market['quote']; return { 'type': takerOrMaker, 'currency': currency, 'rate': rate, 'cost': parseFloat (this.currencyToPrecision (currency, rate * cost)), }; } async fetchPaymentMethods (params = {}) { return await this.privateGetPaymentMethods (params); } async deposit (code, amount, address, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'amount': amount, }; let method = 'privatePostDeposits'; if ('payment_method_id' in params) { // deposit from a payment_method, like a bank account method += 'PaymentMethod'; } else if ('coinbase_account_id' in params) { // deposit into Coinbase Pro account from a Coinbase account method += 'CoinbaseAccount'; } else { // deposit methodotherwise we did not receive a supported deposit location // relevant docs link for the Googlers // https://docs.pro.coinbase.com/#deposits throw new NotSupported (this.id + ' deposit() requires one of `coinbase_account_id` or `payment_method_id` extra params'); } const response = await this[method] (this.extend (request, params)); if (!response) { throw new ExchangeError (this.id + ' deposit() error: ' + this.json (response)); } return { 'info': response, 'id': response['id'], }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'amount': amount, }; let method = 'privatePostWithdrawals'; if ('payment_method_id' in params) { method += 'PaymentMethod'; } else if ('coinbase_account_id' in params) { method += 'CoinbaseAccount'; } else { method += 'Crypto'; request['crypto_address'] = address; } const response = await this[method] (this.extend (request, params)); if (!response) { throw new ExchangeError (this.id + ' withdraw() error: ' + this.json (response)); } return { 'info': response, 'id': response['id'], }; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); await this.loadAccounts (); let currency = undefined; let id = this.safeString (params, 'id'); // account id if (id === undefined) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency code argument if no account id specified in params'); } currency = this.currency (code); const accountsByCurrencyCode = this.indexBy (this.accounts, 'currency'); const account = this.safeValue (accountsByCurrencyCode, code); if (account === undefined) { throw new ExchangeError (this.id + ' fetchTransactions() could not find account id for ' + code); } id = account['id']; } const request = { 'id': id, }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetAccountsIdTransfers (this.extend (request, params)); for (let i = 0; i < response.length; i++) { response[i]['currency'] = code; } return this.parseTransactions (response, currency, since, limit); } parseTransactionStatus (transaction) { const canceled = this.safeValue (transaction, 'canceled_at'); if (canceled) { return 'canceled'; } const processed = this.safeValue (transaction, 'processed_at'); const completed = this.safeValue (transaction, 'completed_at'); if (completed) { return 'ok'; } else if (processed && !completed) { return 'failed'; } else { return 'pending'; } } parseTransaction (transaction, currency = undefined) { const details = this.safeValue (transaction, 'details', {}); const id = this.safeString (transaction, 'id'); const txid = this.safeString (details, 'crypto_transaction_hash'); const timestamp = this.parse8601 (this.safeString (transaction, 'created_at')); const updated = this.parse8601 (this.safeString (transaction, 'processed_at')); const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const fee = undefined; const status = this.parseTransactionStatus (transaction); const amount = this.safeFloat (transaction, 'amount'); let type = this.safeString (transaction, 'type'); let address = this.safeString (details, 'crypto_address'); const tag = this.safeString (details, 'destination_tag'); address = this.safeString (transaction, 'crypto_address', address); if (type === 'withdraw') { type = 'withdrawal'; address = this.safeString (details, 'sent_to_address', address); } return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': updated, 'fee': fee, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (method === 'GET') { if (Object.keys (query).length) { request += '?' + this.urlencode (query); } } const url = this.urls['api'][api] + request; if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); let payload = ''; if (method !== 'GET') { if (Object.keys (query).length) { body = this.json (query); payload = body; } } const what = nonce + method + request + payload; const secret = this.base64ToBinary (this.secret); const signature = this.hmac (this.encode (what), secret, 'sha256', 'base64'); headers = { 'CB-ACCESS-KEY': this.apiKey, 'CB-ACCESS-SIGN': this.decode (signature), 'CB-ACCESS-TIMESTAMP': nonce, 'CB-ACCESS-PASSPHRASE': this.password, 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); let accounts = this.safeValue (this.options, 'coinbaseAccounts'); if (accounts === undefined) { accounts = await this.privateGetCoinbaseAccounts (); this.options['coinbaseAccounts'] = accounts; // cache it this.options['coinbaseAccountsByCurrencyId'] = this.indexBy (accounts, 'currency'); } const currencyId = currency['id']; const account = this.safeValue (this.options['coinbaseAccountsByCurrencyId'], currencyId); if (account === undefined) { // eslint-disable-next-line quotes throw new InvalidAddress (this.id + " fetchDepositAddress() could not find currency code " + code + " with id = " + currencyId + " in this.options['coinbaseAccountsByCurrencyId']"); } const request = { 'id': account['id'], }; const response = await this.privateGetCoinbaseAccountsIdAddresses (this.extend (request, params)); const address = this.safeString (response, 'address'); const tag = this.safeString (response, 'destination_tag'); return { 'currency': code, 'address': this.checkAddress (address), 'tag': tag, 'info': response, }; } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); let accounts = this.safeValue (this.options, 'coinbaseAccounts'); if (accounts === undefined) { accounts = await this.privateGetCoinbaseAccounts (); this.options['coinbaseAccounts'] = accounts; // cache it this.options['coinbaseAccountsByCurrencyId'] = this.indexBy (accounts, 'currency'); } const currencyId = currency['id']; const account = this.safeValue (this.options['coinbaseAccountsByCurrencyId'], currencyId); if (account === undefined) { // eslint-disable-next-line quotes throw new InvalidAddress (this.id + " fetchDepositAddress() could not find currency code " + code + " with id = " + currencyId + " in this.options['coinbaseAccountsByCurrencyId']"); } const request = { 'id': account['id'], }; const response = await this.privatePostCoinbaseAccountsIdAddresses (this.extend (request, params)); const address = this.safeString (response, 'address'); const tag = this.safeString (response, 'destination_tag'); return { 'currency': code, 'address': this.checkAddress (address), 'tag': tag, 'info': response, }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if ((code === 400) || (code === 404)) { if (body[0] === '{') { const message = this.safeString (response, 'message'); const feedback = this.id + ' ' + message; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } throw new ExchangeError (this.id + ' ' + body); } } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if (typeof response !== 'string') { if ('message' in response) { throw new ExchangeError (this.id + ' ' + this.json (response)); } } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],63:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadSymbol, ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coincheck extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coincheck', 'name': 'coincheck', 'countries': [ 'JP', 'ID' ], 'rateLimit': 1500, 'has': { 'CORS': false, 'fetchOpenOrders': true, 'fetchMyTrades': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766464-3b5c3c74-5ed9-11e7-840e-31b32968e1da.jpg', 'api': 'https://coincheck.com/api', 'www': 'https://coincheck.com', 'doc': 'https://coincheck.com/documents/exchange/api', 'fees': [ 'https://coincheck.com/exchange/fee', 'https://coincheck.com/info/fee', ], }, 'api': { 'public': { 'get': [ 'exchange/orders/rate', 'order_books', 'rate/{pair}', 'ticker', 'trades', ], }, 'private': { 'get': [ 'accounts', 'accounts/balance', 'accounts/leverage_balance', 'bank_accounts', 'deposit_money', 'exchange/orders/opens', 'exchange/orders/transactions', 'exchange/orders/transactions_pagination', 'exchange/leverage/positions', 'lending/borrows/matches', 'send_money', 'withdraws', ], 'post': [ 'bank_accounts', 'deposit_money/{id}/fast', 'exchange/orders', 'exchange/transfers/to_leverage', 'exchange/transfers/from_leverage', 'lending/borrows', 'lending/borrows/{id}/repay', 'send_money', 'withdraws', ], 'delete': [ 'bank_accounts/{id}', 'exchange/orders/{id}', 'withdraws/{id}', ], }, }, 'markets': { 'BTC/JPY': { 'id': 'btc_jpy', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy' }, // the only real pair // 'ETH/JPY': { 'id': 'eth_jpy', 'symbol': 'ETH/JPY', 'base': 'ETH', 'quote': 'JPY', 'baseId': 'eth', 'quoteId': 'jpy' }, // 'ETC/JPY': { 'id': 'etc_jpy', 'symbol': 'ETC/JPY', 'base': 'ETC', 'quote': 'JPY', 'baseId': 'etc', 'quoteId': 'jpy' }, // 'DAO/JPY': { 'id': 'dao_jpy', 'symbol': 'DAO/JPY', 'base': 'DAO', 'quote': 'JPY', 'baseId': 'dao', 'quoteId': 'jpy' }, // 'LSK/JPY': { 'id': 'lsk_jpy', 'symbol': 'LSK/JPY', 'base': 'LSK', 'quote': 'JPY', 'baseId': 'lsk', 'quoteId': 'jpy' }, // 'FCT/JPY': { 'id': 'fct_jpy', 'symbol': 'FCT/JPY', 'base': 'FCT', 'quote': 'JPY', 'baseId': 'fct', 'quoteId': 'jpy' }, // 'XMR/JPY': { 'id': 'xmr_jpy', 'symbol': 'XMR/JPY', 'base': 'XMR', 'quote': 'JPY', 'baseId': 'xmr', 'quoteId': 'jpy' }, // 'REP/JPY': { 'id': 'rep_jpy', 'symbol': 'REP/JPY', 'base': 'REP', 'quote': 'JPY', 'baseId': 'rep', 'quoteId': 'jpy' }, // 'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' }, // 'ZEC/JPY': { 'id': 'zec_jpy', 'symbol': 'ZEC/JPY', 'base': 'ZEC', 'quote': 'JPY', 'baseId': 'zec', 'quoteId': 'jpy' }, // 'XEM/JPY': { 'id': 'xem_jpy', 'symbol': 'XEM/JPY', 'base': 'XEM', 'quote': 'JPY', 'baseId': 'xem', 'quoteId': 'jpy' }, // 'LTC/JPY': { 'id': 'ltc_jpy', 'symbol': 'LTC/JPY', 'base': 'LTC', 'quote': 'JPY', 'baseId': 'ltc', 'quoteId': 'jpy' }, // 'DASH/JPY': { 'id': 'dash_jpy', 'symbol': 'DASH/JPY', 'base': 'DASH', 'quote': 'JPY', 'baseId': 'dash', 'quoteId': 'jpy' }, // 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' }, // 'ETC/BTC': { 'id': 'etc_btc', 'symbol': 'ETC/BTC', 'base': 'ETC', 'quote': 'BTC', 'baseId': 'etc', 'quoteId': 'btc' }, // 'LSK/BTC': { 'id': 'lsk_btc', 'symbol': 'LSK/BTC', 'base': 'LSK', 'quote': 'BTC', 'baseId': 'lsk', 'quoteId': 'btc' }, // 'FCT/BTC': { 'id': 'fct_btc', 'symbol': 'FCT/BTC', 'base': 'FCT', 'quote': 'BTC', 'baseId': 'fct', 'quoteId': 'btc' }, // 'XMR/BTC': { 'id': 'xmr_btc', 'symbol': 'XMR/BTC', 'base': 'XMR', 'quote': 'BTC', 'baseId': 'xmr', 'quoteId': 'btc' }, // 'REP/BTC': { 'id': 'rep_btc', 'symbol': 'REP/BTC', 'base': 'REP', 'quote': 'BTC', 'baseId': 'rep', 'quoteId': 'btc' }, // 'XRP/BTC': { 'id': 'xrp_btc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc' }, // 'ZEC/BTC': { 'id': 'zec_btc', 'symbol': 'ZEC/BTC', 'base': 'ZEC', 'quote': 'BTC', 'baseId': 'zec', 'quoteId': 'btc' }, // 'XEM/BTC': { 'id': 'xem_btc', 'symbol': 'XEM/BTC', 'base': 'XEM', 'quote': 'BTC', 'baseId': 'xem', 'quoteId': 'btc' }, // 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, // 'DASH/BTC': { 'id': 'dash_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'dash', 'quoteId': 'btc' }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0, 'taker': 0, }, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const balances = await this.privateGetAccountsBalance (params); const result = { 'info': balances }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currencyId = this.currencyId (code); if (currencyId in balances) { const account = this.account (); const reserved = currencyId + '_reserved'; account['free'] = this.safeFloat (balances, currencyId); account['used'] = this.safeFloat (balances, reserved); result[code] = account; } } return this.parseBalance (result); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); // Only BTC/JPY is meaningful let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const response = await this.privateGetExchangeOrdersOpens (params); const rawOrders = this.safeValue (response, 'orders', []); const parsedOrders = this.parseOrders (rawOrders, market, since, limit); const result = []; for (let i = 0; i < parsedOrders.length; i++) { result.push (this.extend (parsedOrders[i], { 'status': 'open' })); } return result; } parseOrder (order, market = undefined) { // // fetchOpenOrders // // { id: 202835, // order_type: "buy", // rate: 26890, // pair: "btc_jpy", // pending_amount: "0.5527", // pending_market_buy_amount: null, // stop_loss_rate: null, // created_at: "2015-01-10T05:55:38.000Z" } // // todo: add formats for fetchOrder, fetchClosedOrders here // const id = this.safeString (order, 'id'); const side = this.safeString (order, 'order_type'); const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const amount = this.safeFloat (order, 'pending_amount'); const remaining = this.safeFloat (order, 'pending_amount'); const price = this.safeFloat (order, 'rate'); let filled = undefined; let cost = undefined; if (remaining !== undefined) { if (amount !== undefined) { filled = Math.max (amount - remaining, 0); if (price !== undefined) { cost = filled * price; } } } const status = undefined; const marketId = this.safeString (order, 'pair'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } return { 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'amount': amount, 'remaining': remaining, 'filled': filled, 'side': side, 'type': undefined, 'status': status, 'symbol': symbol, 'price': price, 'cost': cost, 'fee': undefined, 'info': order, 'average': undefined, 'trades': undefined, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { if (symbol !== 'BTC/JPY') { throw new BadSymbol (this.id + ' fetchOrderBook () supports BTC/JPY only'); } await this.loadMarkets (); const response = await this.publicGetOrderBooks (params); return this.parseOrderBook (response); } async fetchTicker (symbol, params = {}) { if (symbol !== 'BTC/JPY') { throw new BadSymbol (this.id + ' fetchTicker () supports BTC/JPY only'); } await this.loadMarkets (); const ticker = await this.publicGetTicker (params); const timestamp = this.safeTimestamp (ticker, 'timestamp'); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); const id = this.safeString (trade, 'id'); const price = this.safeFloat (trade, 'rate'); const marketId = this.safeString (trade, 'pair'); market = this.safeValue (this.markets_by_id, marketId, market); let symbol = undefined; let baseId = undefined; let quoteId = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; baseId = market['baseId']; quoteId = market['quoteId']; symbol = market['symbol']; } else { const ids = marketId.split ('_'); baseId = ids[0]; quoteId = ids[1]; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } let takerOrMaker = undefined; let amount = undefined; let cost = undefined; let side = undefined; let fee = undefined; let orderId = undefined; if ('liquidity' in trade) { if (this.safeString (trade, 'liquidity') === 'T') { takerOrMaker = 'taker'; } else if (this.safeString (trade, 'liquidity') === 'M') { takerOrMaker = 'maker'; } const funds = this.safeValue (trade, 'funds', {}); amount = this.safeFloat (funds, baseId); cost = this.safeFloat (funds, quoteId); fee = { 'currency': this.safeString (trade, 'fee_currency'), 'cost': this.safeFloat (trade, 'fee'), }; side = this.safeString (trade, 'side'); orderId = this.safeString (trade, 'order_id'); } else { amount = this.safeFloat (trade, 'amount'); side = this.safeString (trade, 'order_type'); } if (cost === undefined) { if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } } return { 'id': id, 'info': trade, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'symbol': symbol, 'type': undefined, 'side': side, 'order': orderId, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const response = await this.privateGetExchangeOrdersTransactions (this.extend ({}, params)); const transactions = this.safeValue (response, 'transactions', []); return this.parseTrades (transactions, market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { if (symbol !== 'BTC/JPY') { throw new BadSymbol (this.id + ' fetchTrades () supports BTC/JPY only'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetTrades (this.extend (request, params)); const data = this.safeValue (response, 'data', []); return this.parseTrades (data, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; if (type === 'market') { const order_type = type + '_' + side; request['order_type'] = order_type; const prefix = (side === 'buy') ? (order_type + '_') : ''; request[prefix + 'amount'] = amount; } else { request['order_type'] = side; request['rate'] = price; request['amount'] = amount; } const response = await this.privatePostExchangeOrders (this.extend (request, params)); const id = this.safeString (response, 'id'); return { 'info': response, 'id': id, }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'id': id, }; return await this.privateDeleteExchangeOrdersId (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); let queryString = ''; if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (this.keysort (query)); } } else { if (Object.keys (query).length) { body = this.urlencode (this.keysort (query)); queryString = body; } } const auth = nonce + url + queryString; headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'ACCESS-KEY': this.apiKey, 'ACCESS-NONCE': nonce, 'ACCESS-SIGNATURE': this.hmac (this.encode (auth), this.encode (this.secret)), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if (api === 'public') { return response; } if ('success' in response) { if (response['success']) { return response; } } throw new ExchangeError (this.id + ' ' + this.json (response)); } }; },{"./base/Exchange":7,"./base/errors":9}],64:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, InvalidNonce, InsufficientFunds, InvalidOrder, OrderNotFound, DDoSProtection } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coinegg extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinegg', 'name': 'CoinEgg', 'countries': [ 'CN', 'UK' ], 'has': { 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': 'emulated', 'fetchMyTrades': false, 'fetchTickers': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/36770310-adfa764e-1c5a-11e8-8e09-449daac3d2fb.jpg', 'api': { 'web': 'https://trade.coinegg.com/web', 'rest': 'https://api.coinegg.com/api/v1', }, 'www': 'https://www.coinegg.com', 'doc': 'https://www.coinegg.com/explain.api.html', 'fees': 'https://www.coinegg.com/fee.html', 'referral': 'https://www.coinegg.com/user/register?invite=523218', }, 'api': { 'web': { 'get': [ 'symbol/ticker?right_coin={quote}', '{quote}/trends', '{quote}/{base}/order', '{quote}/{base}/trades', '{quote}/{base}/depth.js', ], }, 'public': { 'get': [ 'ticker/region/{quote}', 'depth/region/{quote}', 'orders/region/{quote}', ], }, 'private': { 'post': [ 'balance', 'trade_add/region/{quote}', 'trade_cancel/region/{quote}', 'trade_view/region/{quote}', 'trade_list/region/{quote}', ], }, }, 'fees': { 'trading': { 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, 'funding': { 'withdraw': { 'BTC': 0.008, 'BCH': 0.002, 'LTC': 0.001, 'ETH': 0.01, 'ETC': 0.01, 'NEO': 0, 'QTUM': '1%', 'XRP': '1%', 'DOGE': '1%', 'LSK': '1%', 'XAS': '1%', 'BTS': '1%', 'GAME': '1%', 'GOOC': '1%', 'NXT': '1%', 'IFC': '1%', 'DNC': '1%', 'BLK': '1%', 'VRC': '1%', 'XPM': '1%', 'VTC': '1%', 'TFC': '1%', 'PLC': '1%', 'EAC': '1%', 'PPC': '1%', 'FZ': '1%', 'ZET': '1%', 'RSS': '1%', 'PGC': '1%', 'SKT': '1%', 'JBC': '1%', 'RIO': '1%', 'LKC': '1%', 'ZCC': '1%', 'MCC': '1%', 'QEC': '1%', 'MET': '1%', 'YTC': '1%', 'HLB': '1%', 'MRYC': '1%', 'MTC': '1%', 'KTC': 0, }, }, }, 'exceptions': { '103': AuthenticationError, '104': AuthenticationError, '105': AuthenticationError, '106': InvalidNonce, '200': InsufficientFunds, '201': InvalidOrder, '202': InvalidOrder, '203': OrderNotFound, '402': DDoSProtection, }, 'errorMessages': { '100': 'Required parameters can not be empty', '101': 'Illegal parameter', '102': 'coin does not exist', '103': 'Key does not exist', '104': 'Signature does not match', '105': 'Insufficient permissions', '106': 'Request expired(nonce error)', '200': 'Lack of balance', '201': 'Too small for the number of trading', '202': 'Price must be in 0 - 1000000', '203': 'Order does not exist', '204': 'Pending order amount must be above 0.001 BTC', '205': 'Restrict pending order prices', '206': 'Decimal place error', '401': 'System error', '402': 'Requests are too frequent', '403': 'Non-open API', '404': 'IP restriction does not request the resource', '405': 'Currency transactions are temporarily closed', }, 'options': { 'quoteIds': [ 'btc', 'eth', 'usc', 'usdt' ], }, 'commonCurrencies': { 'JBC': 'JubaoCoin', }, }); } async fetchMarkets (params = {}) { const quoteIds = this.options['quoteIds']; const result = []; for (let b = 0; b < quoteIds.length; b++) { const quoteId = quoteIds[b]; const response = await this.webGetSymbolTickerRightCoinQuote ({ 'quote': quoteId, }); const tickers = this.safeValue (response, 'data', []); for (let i = 0; i < tickers.length; i++) { const ticker = tickers[i]; const id = ticker['symbol']; const baseId = id.split ('_')[0]; let base = baseId.toUpperCase (); let quote = quoteId.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const symbol = base + '/' + quote; const precision = { 'amount': 8, 'price': 8, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': ticker, }); } } return result; } parseTicker (ticker, market = undefined) { const symbol = market['symbol']; const timestamp = this.milliseconds (); const last = this.safeFloat (ticker, 'last'); const percentage = this.safeFloat (ticker, 'change'); let open = undefined; let change = undefined; let average = undefined; if (percentage !== undefined) { const relativeChange = percentage / 100; open = last / this.sum (1, relativeChange); change = last - open; average = this.sum (last, open) / 2; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, 'quoteVol'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin': market['baseId'], 'quote': market['quoteId'], }; const response = await this.publicGetTickerRegionQuote (this.extend (request, params)); return this.parseTicker (response, market); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin': market['baseId'], 'quote': market['quoteId'], }; const response = await this.publicGetDepthRegionQuote (this.extend (request, params)); return this.parseOrderBook (response); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); const symbol = market['symbol']; let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = this.costToPrecision (symbol, price * amount); } } const type = 'limit'; const side = this.safeString (trade, 'type'); const id = this.safeString (trade, 'tid'); return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin': market['baseId'], 'quote': market['quoteId'], }; const response = await this.publicGetOrdersRegionQuote (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostBalance (params); const result = { 'info': response }; const data = this.safeValue (response, 'data', {}); const balances = this.omit (data, 'uid'); const keys = Object.keys (balances); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const [ currencyId, accountType ] = key.split ('_'); const code = this.safeCurrencyCode (currencyId); if (!(code in result)) { result[code] = this.account (); } const type = (accountType === 'lock') ? 'used' : 'free'; result[code][type] = this.safeFloat (balances, key); } return this.parseBalance (result); } parseOrder (order, market = undefined) { let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString (order, 'datetime')); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount_original'); const remaining = this.safeFloat (order, 'amount_outstanding'); let filled = undefined; if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; } } let status = this.safeString (order, 'status'); if (status === 'cancelled') { status = 'canceled'; } else { status = remaining ? 'open' : 'closed'; } const info = this.safeValue (order, 'info', order); const type = 'limit'; const side = this.safeString (order, 'type'); const id = this.safeString (order, 'id'); return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': undefined, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': undefined, 'info': info, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin': market['baseId'], 'quote': market['quoteId'], 'type': side, 'amount': amount, 'price': price, }; const response = await this.privatePostTradeAddRegionQuote (this.extend (request, params)); const id = this.safeString (response, 'id'); const order = this.parseOrder ({ 'id': id, 'datetime': this.ymdhms (this.milliseconds ()), 'amount_original': amount, 'amount_outstanding': amount, 'price': price, 'type': side, 'info': response, }, market); this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': id, 'coin': market['baseId'], 'quote': market['quoteId'], }; return await this.privatePostTradeCancelRegionQuote (this.extend (request, params)); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': id, 'coin': market['baseId'], 'quote': market['quoteId'], }; const response = await this.privatePostTradeViewRegionQuote (this.extend (request, params)); return this.parseOrder (response['data'], market); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin': market['baseId'], 'quote': market['quoteId'], }; if (since !== undefined) { request['since'] = since / 1000; } const response = await this.privatePostTradeListRegionQuote (this.extend (request, params)); return this.parseOrders (response['data'], market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'type': 'open', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let apiType = 'rest'; if (api === 'web') { apiType = api; } let url = this.urls['api'][apiType] + '/' + this.implodeParams (path, params); let query = this.omit (params, this.extractParams (path)); if (api === 'public' || api === 'web') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); query = this.urlencode (this.extend ({ 'key': this.apiKey, 'nonce': this.nonce (), }, query)); const secret = this.hash (this.encode (this.secret)); const signature = this.hmac (this.encode (query), this.encode (secret)); query += '&' + 'signature=' + signature; if (method === 'GET') { url += '?' + query; } else { headers = { 'Content-type': 'application/x-www-form-urlencoded', }; body = query; } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } // private endpoints return the following structure: // {"result":true,"data":{...}} - success // {"result":false,"code":"103"} - failure // {"code":0,"msg":"Suceess","data":{"uid":"2716039","btc_balance":"0.00000000","btc_lock":"0.00000000","xrp_balance":"0.00000000","xrp_lock":"0.00000000"}} const result = this.safeValue (response, 'result'); if (result === undefined) { // public endpoint ← this comment left here by the contributor, in fact a missing result does not necessarily mean a public endpoint... // we should just check the code and don't rely on the result at all here... return; } if (result === true) { // success return; } const errorCode = this.safeString (response, 'code'); const errorMessages = this.errorMessages; const message = this.safeString (errorMessages, errorCode, 'Unknown Error'); const feedback = this.id + ' ' + message; this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); throw new ExchangeError (this.id + ' ' + message); } }; },{"./base/Exchange":7,"./base/errors":9}],65:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, InsufficientFunds, OrderNotFound, InvalidOrder, AuthenticationError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coinex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinex', 'name': 'CoinEx', 'version': 'v1', 'countries': [ 'CN' ], 'rateLimit': 1000, 'has': { 'fetchTickers': true, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'withdraw': true, 'fetchDeposits': true, 'fetchWithdrawals': true, }, 'timeframes': { '1m': '1min', '3m': '3min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '2h': '2hour', '4h': '4hour', '6h': '6hour', '12h': '12hour', '1d': '1day', '3d': '3day', '1w': '1week', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/38046312-0b450aac-32c8-11e8-99ab-bc6b136b6cc7.jpg', 'api': 'https://api.coinex.com', 'www': 'https://www.coinex.com', 'doc': 'https://github.com/coinexcom/coinex_exchange_api/wiki', 'fees': 'https://www.coinex.com/fees', 'referral': 'https://www.coinex.com/register?refer_code=yw5fz', }, 'api': { 'public': { 'get': [ 'common/currency/rate', 'common/asset/config', 'market/info', 'market/list', 'market/ticker', 'market/ticker/all', 'market/depth', 'market/deals', 'market/kline', ], }, 'private': { 'get': [ 'balance/coin/deposit', 'balance/coin/withdraw', 'balance/info', 'future/account', 'future/config', 'future/limitprice', 'future/loan/history', 'future/market', 'margin/account', 'margin/config', 'margin/loan/history', 'margin/market', 'order', 'order/deals', 'order/finished', 'order/finished/{id}', 'order/pending', 'order/status', 'order/status/batch', 'order/user/deals', ], 'post': [ 'balance/coin/withdraw', 'future/flat', 'future/loan', 'future/transfer', 'margin/flat', 'margin/loan', 'margin/transfer', 'order/batchlimit', 'order/ioc', 'order/limit', 'order/market', 'sub_account/transfer', ], 'delete': [ 'balance/coin/withdraw', 'order/pending/batch', 'order/pending', ], }, }, 'fees': { 'trading': { 'maker': 0.001, 'taker': 0.001, }, 'funding': { 'withdraw': { 'BCH': 0.0, 'BTC': 0.001, 'LTC': 0.001, 'ETH': 0.001, 'ZEC': 0.0001, 'DASH': 0.0001, }, }, }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined, }, }, 'precision': { 'amount': 8, 'price': 8, }, 'options': { 'createMarketBuyOrderRequiresPrice': true, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetMarketInfo (params); // // { // "code": 0, // "data": { // "WAVESBTC": { // "name": "WAVESBTC", // "min_amount": "1", // "maker_fee_rate": "0.001", // "taker_fee_rate": "0.001", // "pricing_name": "BTC", // "pricing_decimal": 8, // "trading_name": "WAVES", // "trading_decimal": 8 // } // } // } // const markets = this.safeValue (response, 'data', {}); const result = []; const keys = Object.keys (markets); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const market = markets[key]; const id = this.safeString (market, 'name'); const tradingName = this.safeString (market, 'trading_name'); const baseId = tradingName; const quoteId = this.safeString (market, 'pricing_name'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); let symbol = base + '/' + quote; if (tradingName === id) { symbol = id; } const precision = { 'amount': this.safeInteger (market, 'trading_decimal'), 'price': this.safeInteger (market, 'pricing_decimal'), }; const active = undefined; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'taker': this.safeFloat (market, 'taker_fee_rate'), 'maker': this.safeFloat (market, 'maker_fee_rate'), 'info': market, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (market, 'min_amount'), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, }, }); } return result; } parseTicker (ticker, market = undefined) { const timestamp = this.safeInteger (ticker, 'date'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } ticker = this.safeValue (ticker, 'ticker', {}); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat2 (ticker, 'vol', 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetMarketTicker (this.extend (request, params)); return this.parseTicker (response['data'], market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarketTickerAll (params); const data = this.safeValue (response, 'data'); const timestamp = this.safeInteger (data, 'date'); const tickers = this.safeValue (data, 'ticker'); const marketIds = Object.keys (tickers); const result = {}; for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; let symbol = marketId; let market = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } const ticker = { 'date': timestamp, 'ticker': tickers[marketId], }; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchOrderBook (symbol, limit = 20, params = {}) { await this.loadMarkets (); if (limit === undefined) { limit = 20; // default } const request = { 'market': this.marketId (symbol), 'merge': '0.0000000001', 'limit': limit.toString (), }; const response = await this.publicGetMarketDepth (this.extend (request, params)); return this.parseOrderBook (response['data']); } parseTrade (trade, market = undefined) { // this method parses both public and private trades let timestamp = this.safeTimestamp (trade, 'create_time'); if (timestamp === undefined) { timestamp = this.safeInteger (trade, 'date_ms'); } const tradeId = this.safeString (trade, 'id'); const orderId = this.safeString (trade, 'order_id'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); const marketId = this.safeString (trade, 'market'); market = this.safeValue (this.markets_by_id, marketId, market); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } let cost = this.safeFloat (trade, 'deal_money'); if (!cost) { cost = parseFloat (this.costToPrecision (symbol, price * amount)); } let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'fee_asset'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } const takerOrMaker = this.safeString (trade, 'role'); const side = this.safeString (trade, 'type'); return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': tradeId, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetMarketDeals (this.extend (request, params)); return this.parseTrades (response['data'], market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, parseFloat (ohlcv[1]), parseFloat (ohlcv[3]), parseFloat (ohlcv[4]), parseFloat (ohlcv[2]), parseFloat (ohlcv[5]), ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'type': this.timeframes[timeframe], }; const response = await this.publicGetMarketKline (this.extend (request, params)); return this.parseOHLCVs (response['data'], market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetBalanceInfo (params); // // { // "code": 0, // "data": { // "BCH": { # BCH account // "available": "13.60109", # Available BCH // "frozen": "0.00000" # Frozen BCH // }, // "BTC": { # BTC account // "available": "32590.16", # Available BTC // "frozen": "7000.00" # Frozen BTC // }, // "ETH": { # ETH account // "available": "5.06000", # Available ETH // "frozen": "0.00000" # Frozen ETH // } // }, // "message": "Ok" // } // const result = { 'info': response }; const balances = this.safeValue (response, 'data'); const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const balance = this.safeValue (balances, currencyId, {}); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'frozen'); result[code] = account; } return this.parseBalance (result); } parseOrderStatus (status) { const statuses = { 'not_deal': 'open', 'part_deal': 'open', 'done': 'closed', 'cancel': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // fetchOrder // // { // "amount": "0.1", // "asset_fee": "0.22736197736197736197", // "avg_price": "196.85000000000000000000", // "create_time": 1537270135, // "deal_amount": "0.1", // "deal_fee": "0", // "deal_money": "19.685", // "fee_asset": "CET", // "fee_discount": "0.5", // "id": 1788259447, // "left": "0", // "maker_fee_rate": "0", // "market": "ETHUSDT", // "order_type": "limit", // "price": "170.00000000", // "status": "done", // "taker_fee_rate": "0.0005", // "type": "sell", // } // const timestamp = this.safeTimestamp (order, 'create_time'); const price = this.safeFloat (order, 'price'); const cost = this.safeFloat (order, 'deal_money'); const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'deal_amount'); const average = this.safeFloat (order, 'avg_price'); let symbol = undefined; const marketId = this.safeString (order, 'market'); market = this.safeValue (this.markets_by_id, marketId); const feeCurrencyId = this.safeString (order, 'fee_asset'); let feeCurrency = this.safeCurrencyCode (feeCurrencyId); if (market !== undefined) { symbol = market['symbol']; if (feeCurrency === undefined) { feeCurrency = market['quote']; } } const remaining = this.safeFloat (order, 'left'); const status = this.parseOrderStatus (this.safeString (order, 'status')); const type = this.safeString (order, 'order_type'); const side = this.safeString (order, 'type'); return { 'id': this.safeString (order, 'id'), 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': { 'currency': feeCurrency, 'cost': this.safeFloat (order, 'deal_fee'), }, 'info': order, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const method = 'privatePostOrder' + this.capitalize (type); const market = this.market (symbol); const request = { 'market': market['id'], 'type': side, }; amount = parseFloat (amount); // for market buy it requires the amount of quote currency to spend if ((type === 'market') && (side === 'buy')) { if (this.options['createMarketBuyOrderRequiresPrice']) { if (price === undefined) { throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); } else { price = parseFloat (price); request['amount'] = this.costToPrecision (symbol, amount * price); } } else { request['amount'] = this.costToPrecision (symbol, amount); } } else { request['amount'] = this.amountToPrecision (symbol, amount); } if ((type === 'limit') || (type === 'ioc')) { request['price'] = this.priceToPrecision (symbol, price); } const response = await this[method] (this.extend (request, params)); const order = this.parseOrder (response['data'], market); const id = order['id']; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': id, 'market': market['id'], }; const response = await this.privateDeleteOrderPending (this.extend (request, params)); return this.parseOrder (response['data'], market); } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': id, 'market': market['id'], }; const response = await this.privateGetOrder (this.extend (request, params)); // // { // "code": 0, // "data": { // "amount": "0.1", // "asset_fee": "0.22736197736197736197", // "avg_price": "196.85000000000000000000", // "create_time": 1537270135, // "deal_amount": "0.1", // "deal_fee": "0", // "deal_money": "19.685", // "fee_asset": "CET", // "fee_discount": "0.5", // "id": 1788259447, // "left": "0", // "maker_fee_rate": "0", // "market": "ETHUSDT", // "order_type": "limit", // "price": "170.00000000", // "status": "done", // "taker_fee_rate": "0.0005", // "type": "sell", // }, // "message": "Ok" // } // return this.parseOrder (response['data'], market); } async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (limit === undefined) { limit = 100; } const request = { 'page': 1, 'limit': limit, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const method = 'privateGetOrder' + this.capitalize (status); const response = await this[method] (this.extend (request, params)); return this.parseOrders (response['data']['data'], market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus ('pending', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus ('finished', symbol, since, limit, params); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (limit === undefined) { limit = 100; } const request = { 'page': 1, 'limit': limit, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const response = await this.privateGetOrderUserDeals (this.extend (request, params)); return this.parseTrades (response['data']['data'], market, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); if (tag) { address = address + ':' + tag; } const request = { 'coin_type': currency['id'], 'coin_address': address, // must be authorized, inter-user transfer by a registered mobile phone number or an email address is supported 'actual_amount': parseFloat (amount), // the actual amount without fees, https://www.coinex.com/fees 'transfer_method': '1', // '1' = normal onchain transfer, '2' = internal local transfer from one user to another }; const response = await this.privatePostBalanceCoinWithdraw (this.extend (request, params)); // // { // "code": 0, // "data": { // "actual_amount": "1.00000000", // "amount": "1.00000000", // "coin_address": "1KAv3pazbTk2JnQ5xTo6fpKK7p1it2RzD4", // "coin_type": "BCH", // "coin_withdraw_id": 206, // "confirmations": 0, // "create_time": 1524228297, // "status": "audit", // "tx_fee": "0", // "tx_id": "" // }, // "message": "Ok" // } // const transaction = this.safeValue (response, 'data', {}); return this.parseTransaction (transaction, currency); } parseTransactionStatus (status) { const statuses = { 'audit': 'pending', 'pass': 'pending', 'processing': 'pending', 'confirming': 'pending', 'not_pass': 'failed', 'cancel': 'canceled', 'finish': 'ok', 'fail': 'failed', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // "actual_amount": "120.00000000", // "actual_amount_display": "120", // "add_explorer": "XXX", // "amount": "120.00000000", // "amount_display": "120", // "coin_address": "XXXXXXXX", // "coin_address_display": "XXXXXXXX", // "coin_deposit_id": 1866, // "coin_type": "USDT", // "confirmations": 0, // "create_time": 1539595701, // "explorer": "", // "remark": "", // "status": "finish", // "status_display": "finish", // "transfer_method": "local", // "tx_id": "", // "tx_id_display": "XXXXXXXXXX" // } // // fetchWithdrawals // // { // "actual_amount": "0.10000000", // "amount": "0.10000000", // "coin_address": "15sr1VdyXQ6sVLqeJUJ1uPzLpmQtgUeBSB", // "coin_type": "BCH", // "coin_withdraw_id": 203, // "confirmations": 11, // "create_time": 1515806440, // "status": "finish", // "tx_fee": "0", // "tx_id": "896371d0e23d64d1cac65a0b7c9e9093d835affb572fec89dd4547277fbdd2f6" // } // const id = this.safeString2 (transaction, 'coin_withdraw_id', 'coin_deposit_id'); const address = this.safeString (transaction, 'coin_address'); let tag = this.safeString (transaction, 'remark'); // set but unused if (tag !== undefined) { if (tag.length < 1) { tag = undefined; } } let txid = this.safeValue (transaction, 'tx_id'); if (txid !== undefined) { if (txid.length < 1) { txid = undefined; } } const currencyId = this.safeString (transaction, 'coin_type'); const code = this.safeCurrencyCode (currencyId, currency); const timestamp = this.safeTimestamp (transaction, 'create_time'); const type = ('coin_withdraw_id' in transaction) ? 'withdraw' : 'deposit'; const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const amount = this.safeFloat (transaction, 'amount'); let feeCost = this.safeFloat (transaction, 'tx_fee'); if (type === 'deposit') { feeCost = 0; } const fee = { 'cost': feeCost, 'currency': code, }; return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': fee, }; } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'coin_type': currency['id'], }; if (limit !== undefined) { request['Limit'] = limit; } const response = await this.privateGetBalanceCoinWithdraw (this.extend (request, params)); // // { // "code": 0, // "data": [ // { // "actual_amount": "1.00000000", // "amount": "1.00000000", // "coin_address": "1KAv3pazbTk2JnQ5xTo6fpKK7p1it2RzD4", // "coin_type": "BCH", // "coin_withdraw_id": 206, // "confirmations": 0, // "create_time": 1524228297, // "status": "audit", // "tx_fee": "0", // "tx_id": "" // }, // { // "actual_amount": "0.10000000", // "amount": "0.10000000", // "coin_address": "15sr1VdyXQ6sVLqeJUJ1uPzLpmQtgUeBSB", // "coin_type": "BCH", // "coin_withdraw_id": 203, // "confirmations": 11, // "create_time": 1515806440, // "status": "finish", // "tx_fee": "0", // "tx_id": "896371d0e23d64d1cac65a0b7c9e9093d835affb572fec89dd4547277fbdd2f6" // }, // { // "actual_amount": "0.00100000", // "amount": "0.00100000", // "coin_address": "1GVVx5UBddLKrckTprNi4VhHSymeQ8tsLF", // "coin_type": "BCH", // "coin_withdraw_id": 27, // "confirmations": 0, // "create_time": 1513933541, // "status": "cancel", // "tx_fee": "0", // "tx_id": "" // } // ], // "message": "Ok" // } // return this.parseTransactions (response['data'], currency, since, limit); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'coin_type': currency['id'], }; if (limit !== undefined) { request['Limit'] = limit; } const response = await this.privateGetBalanceCoinDeposit (this.extend (request, params)); // { // "code": 0, // "data": [ // { // "actual_amount": "4.65397682", // "actual_amount_display": "4.65397682", // "add_explorer": "https://etherscan.io/address/0x361XXXXXX", // "amount": "4.65397682", // "amount_display": "4.65397682", // "coin_address": "0x36dabcdXXXXXX", // "coin_address_display": "0x361X*****XXXXX", // "coin_deposit_id": 966191, // "coin_type": "ETH", // "confirmations": 30, // "create_time": 1531661445, // "explorer": "https://etherscan.io/tx/0x361XXXXXX", // "remark": "", // "status": "finish", // "status_display": "finish", // "transfer_method": "onchain", // "tx_id": "0x361XXXXXX", // "tx_id_display": "0x361XXXXXX" // } // ], // "message": "Ok" // } // return this.parseTransactions (response['data'], currency, since, limit); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { path = this.implodeParams (path, params); let url = this.urls['api'] + '/' + this.version + '/' + path; let query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); query = this.extend ({ 'access_id': this.apiKey, 'tonce': nonce.toString (), }, query); query = this.keysort (query); const urlencoded = this.urlencode (query); const signature = this.hash (this.encode (urlencoded + '&secret_key=' + this.secret)); headers = { 'Authorization': signature.toUpperCase (), 'Content-Type': 'application/json', }; if ((method === 'GET') || (method === 'DELETE')) { url += '?' + urlencoded; } else { body = this.json (query); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); const code = this.safeString (response, 'code'); const data = this.safeValue (response, 'data'); const message = this.safeString (response, 'message'); if ((code !== '0') || (data === undefined) || ((message !== 'Ok') && !data)) { const responseCodes = { '24': AuthenticationError, '25': AuthenticationError, '107': InsufficientFunds, '600': OrderNotFound, '601': InvalidOrder, '602': InvalidOrder, '606': InvalidOrder, }; const ErrorClass = this.safeValue (responseCodes, code, ExchangeError); throw new ErrorClass (response['message']); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],66:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, RateLimitExceeded, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coinfalcon extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinfalcon', 'name': 'CoinFalcon', 'countries': [ 'GB' ], 'rateLimit': 1000, 'version': 'v1', 'has': { 'fetchTickers': true, 'fetchOpenOrders': true, 'fetchMyTrades': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/41822275-ed982188-77f5-11e8-92bb-496bcd14ca52.jpg', 'api': 'https://coinfalcon.com', 'www': 'https://coinfalcon.com', 'doc': 'https://docs.coinfalcon.com', 'fees': 'https://coinfalcon.com/fees', 'referral': 'https://coinfalcon.com/?ref=CFJSVGTUPASB', }, 'api': { 'public': { 'get': [ 'markets', 'markets/{market}/orders', 'markets/{market}/trades', ], }, 'private': { 'get': [ 'user/accounts', 'user/orders', 'user/orders/{id}', 'user/trades', ], 'post': [ 'user/orders', ], 'delete': [ 'user/orders/{id}', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'maker': 0.0, 'taker': 0.002, // tiered fee starts at 0.2% }, }, 'precision': { 'amount': 8, 'price': 8, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetMarkets (params); const markets = this.safeValue (response, 'data'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const [ baseId, quoteId ] = market['name'].split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'size_precision'), 'price': this.safeInteger (market, 'price_precision'), }; result.push ({ 'id': market['name'], 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }); } return result; } parseTicker (ticker, market = undefined) { if (market === undefined) { const marketId = this.safeString (ticker, 'name'); market = this.safeValue (this.markets_by_id, marketId, market); } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.milliseconds (); const last = parseFloat (ticker['last_price']); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeFloat (ticker, 'change_in_24h'), 'percentage': undefined, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeFloat (ticker, 'volume'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const tickers = await this.fetchTickers (params); return tickers[symbol]; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarkets (params); const tickers = this.safeValue (response, 'data'); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = this.parseTicker (tickers[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), 'level': '3', }; const response = await this.publicGetMarketsMarketOrders (this.extend (request, params)); return this.parseOrderBook (response['data'], undefined, 'bids', 'asks', 'price', 'size'); } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'size'); const symbol = market['symbol']; let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = parseFloat (this.costToPrecision (symbol, price * amount)); } } const tradeId = this.safeString (trade, 'id'); const side = this.safeString (trade, 'side'); const orderId = this.safeString (trade, 'order_id'); let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrencyCode = this.safeString (trade, 'fee_currency_code'); fee = { 'cost': feeCost, 'currency': this.safeCurrencyCode (feeCurrencyCode), }; } return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': tradeId, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (since !== undefined) { request['start_time'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetUserTrades (this.extend (request, params)); return this.parseTrades (response['data'], market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (since !== undefined) { request['since'] = this.iso8601 (since); } const response = await this.publicGetMarketsMarketTrades (this.extend (request, params)); return this.parseTrades (response['data'], market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetUserAccounts (params); const result = { 'info': response }; const balances = this.safeValue (response, 'data'); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency_code'); const code = this.safeCurrencyCode (currencyId); const account = { 'free': this.safeFloat (balance, 'available_balance'), 'used': this.safeFloat (balance, 'hold_balance'), 'total': this.safeFloat (balance, 'balance'), }; result[code] = account; } return this.parseBalance (result); } parseOrderStatus (status) { const statuses = { 'fulfilled': 'closed', 'canceled': 'canceled', 'pending': 'open', 'open': 'open', 'partially_filled': 'open', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "id":"8bdd79f4-8414-40a2-90c3-e9f4d6d1eef4" // "market":"IOT-BTC" // "price":"0.0000003" // "size":"4.0" // "size_filled":"3.0" // "fee":"0.0075" // "fee_currency_code":"iot" // "funds":"0.0" // "status":"canceled" // "order_type":"buy" // "post_only":false // "operation_type":"market_order" // "created_at":"2018-01-12T21:14:06.747828Z" // } // if (market === undefined) { const marketId = this.safeString (order, 'market'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'size'); const filled = this.safeFloat (order, 'size_filled'); let remaining = undefined; let cost = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); } if (price !== undefined) { cost = parseFloat (this.priceToPrecision (symbol, filled * price)); } } const status = this.parseOrderStatus (this.safeString (order, 'status')); let type = this.safeString (order, 'operation_type'); if (type !== undefined) { type = type.split ('_'); type = type[0]; } const side = this.safeString (order, 'order_type'); return { 'id': this.safeString (order, 'id'), 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': undefined, 'info': order, 'lastTradeTimestamp': undefined, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); // price/size must be string const request = { 'market': market['id'], 'size': this.amountToPrecision (symbol, amount), 'order_type': side, }; if (type === 'limit') { price = this.priceToPrecision (symbol, price); request['price'] = price.toString (); } request['operation_type'] = type + '_order'; const response = await this.privatePostUserOrders (this.extend (request, params)); const order = this.parseOrder (response['data'], market); const id = order['id']; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privateDeleteUserOrdersId (this.extend (request, params)); const market = this.market (symbol); return this.parseOrder (response['data'], market); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privateGetUserOrdersId (this.extend (request, params)); return this.parseOrder (response['data']); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbol !== undefined) { request['market'] = this.marketId (symbol); } if (since !== undefined) { request['since_time'] = this.iso8601 (this.milliseconds ()); } // TODO: test status=all if it works for closed orders too const response = await this.privateGetUserOrders (this.extend (request, params)); return this.parseOrders (response['data']); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/api/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { request += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); if (method === 'GET') { if (Object.keys (query).length) { request += '?' + this.urlencode (query); } } else { body = this.json (query); } const seconds = this.seconds ().toString (); let payload = [ seconds, method, request ].join ('|'); if (body) { payload += '|' + body; } const signature = this.hmac (this.encode (payload), this.encode (this.secret)); headers = { 'CF-API-KEY': this.apiKey, 'CF-API-TIMESTAMP': seconds, 'CF-API-SIGNATURE': signature, 'Content-Type': 'application/json', }; } const url = this.urls['api'] + request; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (code < 400) { return; } const ErrorClass = this.safeValue ({ '401': AuthenticationError, '429': RateLimitExceeded, }, code, ExchangeError); throw new ErrorClass (body); } }; },{"./base/Exchange":7,"./base/errors":9}],67:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { InsufficientFunds, ExchangeError, InvalidNonce, InvalidOrder, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coinfloor extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinfloor', 'name': 'coinfloor', 'rateLimit': 1000, 'countries': [ 'UK' ], 'has': { 'CORS': false, 'fetchOpenOrders': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/28246081-623fc164-6a1c-11e7-913f-bac0d5576c90.jpg', 'api': 'https://webapi.coinfloor.co.uk/bist', 'www': 'https://www.coinfloor.co.uk', 'doc': [ 'https://github.com/coinfloor/api', 'https://www.coinfloor.co.uk/api', ], }, 'requiredCredentials': { 'apiKey': true, 'secret': false, 'password': true, 'uid': true, }, 'api': { 'public': { 'get': [ '{id}/ticker/', '{id}/order_book/', '{id}/transactions/', ], }, 'private': { 'post': [ '{id}/balance/', '{id}/user_transactions/', '{id}/open_orders/', '{symbol}/cancel_order/', '{id}/buy/', '{id}/sell/', '{id}/buy_market/', '{id}/sell_market/', '{id}/estimate_sell_market/', '{id}/estimate_buy_market/', ], }, }, 'markets': { 'BTC/GBP': { 'id': 'XBT/GBP', 'symbol': 'BTC/GBP', 'base': 'BTC', 'quote': 'GBP', 'baseId': 'XBT', 'quoteId': 'GBP', 'precision': { 'price': 0, 'amount': 4 }}, 'BTC/EUR': { 'id': 'XBT/EUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'XBT', 'quoteId': 'EUR', 'precision': { 'price': 0, 'amount': 4 }}, }, 'exceptions': { 'exact': { 'You have insufficient funds.': InsufficientFunds, 'Tonce is out of sequence.': InvalidNonce, }, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); let market = undefined; let query = params; const symbol = this.safeString (params, 'symbol'); if (symbol !== undefined) { market = this.market (params['symbol']); query = this.omit (params, 'symbol'); } const marketId = this.safeString (params, 'id'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if (market === undefined) { throw new ArgumentsRequired (this.id + ' fetchBalance requires a symbol param'); } const request = { 'id': market['id'], }; const response = await this.privatePostIdBalance (this.extend (request, query)); const result = { 'info': response, }; // base/quote used for keys e.g. "xbt_reserved" const base = market['base']; const quote = market['quote']; const baseIdLower = this.safeStringLower (market, 'baseId'); const quoteIdLower = this.safeStringLower (market, 'quoteId'); result[base] = { 'free': this.safeFloat (response, baseIdLower + '_available'), 'used': this.safeFloat (response, baseIdLower + '_reserved'), 'total': this.safeFloat (response, baseIdLower + '_balance'), }; result[quote] = { 'free': this.safeFloat (response, quoteIdLower + '_available'), 'used': this.safeFloat (response, quoteIdLower + '_reserved'), 'total': this.safeFloat (response, quoteIdLower + '_balance'), }; return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const response = await this.publicGetIdOrderBook (this.extend (request, params)); return this.parseOrderBook (response); } parseTicker (ticker, market = undefined) { // rewrite to get the timestamp from HTTP headers const timestamp = this.milliseconds (); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const vwap = this.safeFloat (ticker, 'vwap'); const baseVolume = this.safeFloat (ticker, 'volume'); let quoteVolume = undefined; if (vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; const response = await this.publicGetIdTicker (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const id = this.safeString (trade, 'tid'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'id': id, 'order': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; const response = await this.publicGetIdTransactions (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { // code is actually a market symbol in this situation, not a currency code await this.loadMarkets (); let market = undefined; if (code !== undefined) { market = this.market (code); if (market === undefined) { throw new ArgumentsRequired (this.id + ' fetchTransactions requires a code argument (a market symbol)'); } } const request = { 'id': market['id'], 'limit': limit, }; const response = await this.privatePostIdUserTransactions (this.extend (request, params)); return this.parseLedger (response, undefined, since, undefined); } parseLedgerEntryStatus (status) { const types = { 'completed': 'ok', }; return this.safeString (types, status, status); } parseLedgerEntryType (type) { const types = { '0': 'transaction', // deposit '1': 'transaction', // withdrawal '2': 'trade', }; return this.safeString (types, type, type); } parseLedgerEntry (item, currency = undefined) { // // trade // // { // "datetime": "2017-07-25 06:41:24", // "id": 1500964884381265, // "type": 2, // "xbt": "0.1000", // "xbt_eur": "2322.00", // "eur": "-232.20", // "fee": "0.00", // "order_id": 84696745 // } // // transaction (withdrawal) // // { // "datetime": "2017-07-25 13:19:46", // "id": 97669, // "type": 1, // "xbt": "-3.0000", // "xbt_eur": null, // "eur": "0", // "fee": "0.0000", // "order_id": null // } // // transaction (deposit) // // { // "datetime": "2017-07-27 16:44:55", // "id": 98277, // "type": 0, // "xbt": "0", // "xbt_eur": null, // "eur": "4970.04", // "fee": "0.00", // "order_id": null // } // const keys = Object.keys (item); let baseId = undefined; let quoteId = undefined; let baseAmount = undefined; let quoteAmount = undefined; for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (key.indexOf ('_') > 0) { const parts = key.split ('_'); const numParts = parts.length; if (numParts === 2) { const tmpBaseAmount = this.safeFloat (item, parts[0]); const tmpQuoteAmount = this.safeFloat (item, parts[1]); if (tmpBaseAmount !== undefined && tmpQuoteAmount !== undefined) { baseId = parts[0]; quoteId = parts[1]; baseAmount = tmpBaseAmount; quoteAmount = tmpQuoteAmount; } } } } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const type = this.parseLedgerEntryType (this.safeString (item, 'type')); const referenceId = this.safeString (item, 'id'); const timestamp = this.parse8601 (this.safeString (item, 'datetime')); let fee = undefined; const feeCost = this.safeFloat (item, 'fee'); const result = { 'id': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'amount': undefined, 'direction': undefined, 'currency': undefined, 'type': type, 'referenceId': referenceId, 'referenceAccount': undefined, 'before': undefined, 'after': undefined, 'status': 'ok', 'fee': fee, 'info': item, }; if (type === 'trade') { // // it's a trade so let's make multiple entries, we have several options: // // if fee is always in quote currency (the exchange uses this) // https://github.com/coinfloor/API/blob/master/IMPL-GUIDE.md#how-fees-affect-trade-quantities // if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': quote, }; } return [ this.extend (result, { 'currency': base, 'amount': Math.abs (baseAmount), 'direction': (baseAmount > 0) ? 'in' : 'out' }), this.extend (result, { 'currency': quote, 'amount': Math.abs (quoteAmount), 'direction': (quoteAmount > 0) ? 'in' : 'out', 'fee': fee }), ]; // // if fee is base or quote depending on buy/sell side // // const baseFee = (baseAmount > 0) ? { 'currency': base, 'cost': feeCost } : undefined; // const quoteFee = (quoteAmount > 0) ? { 'currency': quote, 'cost': feeCost } : undefined; // return [ // this.extend (result, { 'currency': base, 'amount': baseAmount, 'direction': (baseAmount > 0) ? 'in' : 'out', 'fee': baseFee }), // this.extend (result, { 'currency': quote, 'amount': quoteAmount, 'direction': (quoteAmount > 0) ? 'in' : 'out', 'fee': quoteFee }), // ]; // // fee as the 3rd item // // return [ // this.extend (result, { 'currency': base, 'amount': baseAmount, 'direction': (baseAmount > 0) ? 'in' : 'out' }), // this.extend (result, { 'currency': quote, 'amount': quoteAmount, 'direction': (quoteAmount > 0) ? 'in' : 'out' }), // this.extend (result, { 'currency': feeCurrency, 'amount': feeCost, 'direction': 'out', 'type': 'fee' }), // ]; // } else { // // it's a regular transaction (deposit or withdrawal) // const amount = (baseAmount === 0) ? quoteAmount : baseAmount; const code = (baseAmount === 0) ? quote : base; const direction = (amount > 0) ? 'in' : 'out'; if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': code, }; } return this.extend (result, { 'currency': code, 'amount': Math.abs (amount), 'direction': direction, 'fee': fee, }); } } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; let method = 'privatePostId' + this.capitalize (side); if (type === 'market') { request['quantity'] = amount; method += 'Market'; } else { request['price'] = price; request['amount'] = amount; } return await this[method] (this.extend (request, params)); } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'id': id, }; const response = await this.privatePostSymbolCancelOrder (request); if (response === 'false') { // unfortunately the exchange does not give much info in the response throw new InvalidOrder (this.id + ' cancel was rejected'); } return response; } parseOrder (order, market = undefined) { const timestamp = this.parse8601 (this.safeString (order, 'datetime')); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let side = undefined; const status = this.safeString (order, 'status'); if (order['type'] === 0) { side = 'buy'; } else if (order['type'] === 1) { side = 'sell'; } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (order, 'id'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'amount': amount, 'filled': undefined, 'remaining': undefined, 'cost': cost, 'fee': undefined, 'average': undefined, 'trades': undefined, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol param'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; const response = await this.privatePostIdOpenOrders (this.extend (request, params)); // { // "amount": "1.0000", // "datetime": "2019-07-12 13:28:16", // "id": 233123443, // "price": "1000.00", // "type": 0 // } return this.parseOrders (response, market, since, limit, { 'status': 'open' }); } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (code < 400) { return; } if (response === undefined) { return; } const message = this.safeString (response, 'error_msg'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); throw new ExchangeError (feedback); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { // curl -k -u '[User ID]/[API key]:[Passphrase]' https://webapi.coinfloor.co.uk:8090/bist/XBT/GBP/balance/ let url = this.urls['api'] + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); const auth = this.uid + '/' + this.apiKey + ':' + this.password; const signature = this.decode (this.stringToBase64 (this.encode (auth))); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ' + signature, }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],68:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coingi extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coingi', 'name': 'Coingi', 'rateLimit': 1000, 'countries': [ 'PA', 'BG', 'CN', 'US' ], // Panama, Bulgaria, China, US 'has': { 'CORS': false, 'fetchTickers': true, }, 'urls': { 'referral': 'https://www.coingi.com/?r=XTPPMC', 'logo': 'https://user-images.githubusercontent.com/1294454/28619707-5c9232a8-7212-11e7-86d6-98fe5d15cc6e.jpg', 'api': { 'www': 'https://coingi.com', 'current': 'https://api.coingi.com', 'user': 'https://api.coingi.com', }, 'www': 'https://coingi.com', 'doc': 'https://coingi.docs.apiary.io', }, 'api': { 'www': { 'get': [ '', ], }, 'current': { 'get': [ 'order-book/{pair}/{askCount}/{bidCount}/{depth}', 'transactions/{pair}/{maxCount}', '24hour-rolling-aggregation', ], }, 'user': { 'post': [ 'balance', 'add-order', 'cancel-order', 'orders', 'transactions', 'create-crypto-withdrawal', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.2 / 100, 'maker': 0.2 / 100, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BTC': 0.001, 'LTC': 0.01, 'DOGE': 2, 'PPC': 0.02, 'VTC': 0.2, 'NMC': 2, 'DASH': 0.002, 'USD': 10, 'EUR': 10, }, 'deposit': { 'BTC': 0, 'LTC': 0, 'DOGE': 0, 'PPC': 0, 'VTC': 0, 'NMC': 0, 'DASH': 0, 'USD': 5, 'EUR': 1, }, }, }, }); } async fetchMarkets (params = {}) { const response = await this.wwwGet (params); const parts = response.split ('do=currencyPairSelector-selectCurrencyPair" class="active">'); const currencyParts = parts[1].split ('
'); const result = []; for (let i = 1; i < currencyParts.length; i++) { const currencyPart = currencyParts[i]; const idParts = currencyPart.split ('
'); let id = idParts[0]; id = id.replace ('/', '-'); id = id.toLowerCase (); const [ baseId, quoteId ] = id.split ('-'); let base = baseId.toUpperCase (); let quote = quoteId.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const symbol = base + '/' + quote; const precision = { 'amount': 8, 'price': 8, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': id, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': 0, 'max': undefined, }, }, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const lowercaseCurrencies = []; const currencies = Object.keys (this.currencies); for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; lowercaseCurrencies.push (currency.toLowerCase ()); } const request = { 'currencies': lowercaseCurrencies.join (','), }; const response = await this.userPostBalance (this.extend (request, params)); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance['currency'], 'name'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); const blocked = this.safeFloat (balance, 'blocked'); const inOrders = this.safeFloat (balance, 'inOrders'); const withdrawing = this.safeFloat (balance, 'withdrawing'); account['used'] = this.sum (blocked, inOrders, withdrawing); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = 512, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'depth': 32, // maximum number of depth range steps 1-32 'askCount': limit, // maximum returned number of asks 1-512 'bidCount': limit, // maximum returned number of bids 1-512 }; const orderbook = await this.currentGetOrderBookPairAskCountBidCountDepth (this.extend (request, params)); return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'baseAmount'); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'highestBid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'lowestAsk'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': undefined, 'last': undefined, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'baseVolume'), 'quoteVolume': this.safeFloat (ticker, 'counterVolume'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.currentGet24hourRollingAggregation (params); const result = {}; for (let t = 0; t < response.length; t++) { const ticker = response[t]; const base = ticker['currencyPair']['base'].toUpperCase (); const quote = ticker['currencyPair']['counter'].toUpperCase (); const symbol = base + '/' + quote; let market = undefined; if (symbol in this.markets) { market = this.markets[symbol]; } result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const tickers = await this.fetchTickers (undefined, params); if (symbol in tickers) { return tickers[symbol]; } throw new ExchangeError (this.id + ' return did not contain ' + symbol); } parseTrade (trade, market = undefined) { const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } const timestamp = this.safeInteger (trade, 'timestamp'); const id = this.safeString (trade, 'id'); const marketId = this.safeString (trade, 'currencyPair'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, // type 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'maxCount': 128, }; const response = await this.currentGetTransactionsPairMaxCount (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'currencyPair': this.marketId (symbol), 'volume': amount, 'price': price, 'orderType': (side === 'buy') ? 0 : 1, }; const response = await this.userPostAddOrder (this.extend (request, params)); return { 'info': response, 'id': response['result'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; return await this.userPostCancelOrder (this.extend (request, params)); } sign (path, api = 'current', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; if (api !== 'www') { url += '/' + api + '/' + this.implodeParams (path, params); } const query = this.omit (params, this.extractParams (path)); if (api === 'current') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'user') { this.checkRequiredCredentials (); const nonce = this.nonce (); const request = this.extend ({ 'token': this.apiKey, 'nonce': nonce, }, query); const auth = nonce.toString () + '$' + this.apiKey; request['signature'] = this.hmac (this.encode (auth), this.encode (this.secret)); body = this.json (request); headers = { 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'current', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if (typeof response !== 'string') { if ('errors' in response) { throw new ExchangeError (this.id + ' ' + this.json (response)); } } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],69:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coinmarketcap extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinmarketcap', 'name': 'CoinMarketCap', 'rateLimit': 10000, 'version': 'v1', 'countries': [ 'US' ], 'has': { 'CORS': true, 'privateAPI': false, 'createOrder': false, 'createMarketOrder': false, 'createLimitOrder': false, 'cancelOrder': false, 'editOrder': false, 'fetchBalance': false, 'fetchOrderBook': false, 'fetchL2OrderBook': false, 'fetchOHLCV': false, 'fetchTrades': false, 'fetchTickers': true, 'fetchCurrencies': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/28244244-9be6312a-69ed-11e7-99c1-7c1797275265.jpg', 'api': { 'public': 'https://api.coinmarketcap.com', 'files': 'https://files.coinmarketcap.com', 'charts': 'https://graph.coinmarketcap.com', }, 'www': 'https://coinmarketcap.com', 'doc': 'https://coinmarketcap.com/api', }, 'requiredCredentials': { 'apiKey': false, 'secret': false, }, 'api': { 'files': { 'get': [ 'generated/stats/global.json', ], }, 'graphs': { 'get': [ 'currencies/{name}/', ], }, 'public': { 'get': [ 'ticker/', 'ticker/{id}/', 'global/', ], }, }, 'currencyCodes': [ 'AUD', 'BRL', 'CAD', 'CHF', 'CNY', 'EUR', 'GBP', 'HKD', 'IDR', 'INR', 'JPY', 'KRW', 'MXN', 'RUB', 'USD', 'BTC', 'ETH', 'LTC', ], }); } async fetchOrderBook (symbol, limit = undefined, params = {}) { throw new ExchangeError ('Fetching order books is not supported by the API of ' + this.id); } currencyCode (base, name) { const currencies = { 'ACChain': 'ACChain', 'AdCoin': 'AdCoin', 'BatCoin': 'BatCoin', 'Bitgem': 'Bitgem', 'BlazeCoin': 'BlazeCoin', 'BlockCAT': 'BlockCAT', 'Blocktrade Token': 'Blocktrade Token', 'Catcoin': 'Catcoin', 'CanYaCoin': 'CanYaCoin', // conflict with CAN (Content and AD Network) 'Comet': 'Comet', // conflict with CMT (CyberMiles) 'CPChain': 'CPChain', 'CrowdCoin': 'CrowdCoin', // conflict with CRC CryCash 'Cryptaur': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 'Cubits': 'Cubits', // conflict with QBT (Qbao) 'DAO.Casino': 'DAO.Casino', // conflict with BET (BetaCoin) 'E-Dinar Coin': 'E-Dinar Coin', // conflict with EDR Endor Protocol and EDRCoin 'EDRcoin': 'EDRcoin', // conflict with EDR Endor Protocol and E-Dinar Coin 'ENTCash': 'ENTCash', // conflict with ENT (Eternity) 'FairCoin': 'FairCoin', // conflict with FAIR (FairGame) https://github.com/ccxt/ccxt/pull/5865 'Fabric Token': 'Fabric Token', // 'GET Protocol': 'GET Protocol', 'Global Tour Coin': 'Global Tour Coin', // conflict with GTC (Game.com) 'GuccioneCoin': 'GuccioneCoin', // conflict with GCC (Global Cryptocurrency) 'HarmonyCoin': 'HarmonyCoin', // conflict with HMC (Hi Mutual Society) 'Harvest Masternode Coin': 'Harvest Masternode Coin', // conflict with HC (HyperCash) 'HOT Token': 'HOT Token', 'Hydro Protocol': 'Hydro Protocol', // conflict with HOT (Holo) 'Huncoin': 'Huncoin', // conflict with HNC (Helleniccoin) 'iCoin': 'iCoin', 'Infinity Economics': 'Infinity Economics', // conflict with XIN (Mixin) 'KingN Coin': 'KingN Coin', // conflict with KNC (Kyber Network) 'LiteBitcoin': 'LiteBitcoin', // conflict with LBTC (LightningBitcoin) 'Maggie': 'Maggie', 'Monarch': 'Monarch', // conflict with MyToken (MT) 'MTC Mesh Network': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025 'IOTA': 'IOTA', // a special case, most exchanges list it as IOTA, therefore we change just the Coinmarketcap instead of changing them all 'NetCoin': 'NetCoin', 'PCHAIN': 'PCHAIN', // conflict with PAI (Project Pai) 'Plair': 'Plair', // conflict with PLA (PLANET) 'PlayChip': 'PlayChip', // conflict with PLA (PLANET) 'Polcoin': 'Polcoin', 'PutinCoin': 'PutinCoin', // conflict with PUT (Profile Utility Token) 'Rcoin': 'Rcoin', // conflict with RCN (Ripio Credit Network) // https://github.com/ccxt/ccxt/issues/6081 // https://github.com/ccxt/ccxt/issues/3365 // https://github.com/ccxt/ccxt/issues/2873 'Themis': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol) 'Menlo One': 'Menlo One', // conflict with Harmony (ONE) 'BigONE Token': 'BigONE Token', // conflict with Harmony (ONE) }; return this.safeValue (currencies, name, base); } async fetchMarkets (params = {}) { const request = { 'limit': 0, }; const response = await this.publicGetTicker (this.extend (request, params)); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const currencies = this.currencyCodes; for (let j = 0; j < currencies.length; j++) { const quote = currencies[j]; const quoteId = quote.toLowerCase (); const baseId = market['id']; const base = this.currencyCode (market['symbol'], market['name']); const symbol = base + '/' + quote; const id = baseId + '/' + quoteId; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': undefined, 'precision': this.precision, 'limits': this.limits, }); } } return result; } async fetchGlobal (currency = 'USD') { await this.loadMarkets (); const request = {}; if (currency) { request['convert'] = currency; } return await this.publicGetGlobal (request); } parseTicker (ticker, market = undefined) { let timestamp = this.safeTimestamp (ticker, 'last_updated'); if (timestamp === undefined) { timestamp = this.milliseconds (); } const change = this.safeFloat (ticker, 'percent_change_24h'); let last = undefined; let symbol = undefined; let volume = undefined; if (market !== undefined) { symbol = market['symbol']; const priceKey = 'price_' + market['quoteId']; last = this.safeFloat (ticker, priceKey); const volumeKey = '24h_volume_' + market['quoteId']; volume = this.safeFloat (ticker, volumeKey); } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': change, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': volume, 'info': ticker, }; } async fetchTickers (currency = 'USD', params = {}) { await this.loadMarkets (); const request = { 'limit': 10000, }; if (currency) { request['convert'] = currency; } const response = await this.publicGetTicker (this.extend (request, params)); const result = {}; for (let t = 0; t < response.length; t++) { const ticker = response[t]; const currencyId = currency.toLowerCase (); const id = ticker['id'] + '/' + currencyId; let symbol = id; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'convert': market['quote'], 'id': market['baseId'], }; const response = await this.publicGetTickerId (this.extend (request, params)); const ticker = response[0]; return this.parseTicker (ticker, market); } async fetchCurrencies (params = {}) { const request = { 'limit': 0, }; const response = await this.publicGetTicker (this.extend (request, params)); const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'symbol'); const name = this.safeString (currency, 'name'); // todo: will need to rethink the fees // to add support for multiple withdrawal/deposit methods and // differentiated fees for each particular method const precision = 8; // default precision, todo: fix "magic constants" const code = this.currencyCode (id, name); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': name, 'active': true, 'fee': undefined, // todo: redesign 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, }; } return result; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('error' in response) { if (response['error']) { throw new ExchangeError (this.id + ' ' + this.json (response)); } } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],70:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, InvalidOrder, OrderNotFound, RateLimitExceeded, InsufficientFunds } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coinmate extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinmate', 'name': 'CoinMate', 'countries': [ 'GB', 'CZ', 'EU' ], // UK, Czech Republic 'rateLimit': 1000, 'has': { 'CORS': true, 'fetchBalance': true, 'fetchOrders': true, 'fetchOrder': true, 'fetchMyTrades': true, 'fetchTransactions': true, 'fetchOpenOrders': true, 'createOrder': true, 'cancelOrder': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27811229-c1efb510-606c-11e7-9a36-84ba2ce412d8.jpg', 'api': 'https://coinmate.io/api', 'www': 'https://coinmate.io', 'fees': 'https://coinmate.io/fees', 'doc': [ 'https://coinmate.docs.apiary.io', 'https://coinmate.io/developers', ], 'referral': 'https://coinmate.io?referral=YTFkM1RsOWFObVpmY1ZjMGREQmpTRnBsWjJJNVp3PT0', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': true, }, 'api': { 'public': { 'get': [ 'orderBook', 'ticker', 'transactions', 'tradingPairs', ], }, 'private': { 'post': [ 'balances', 'bitcoinCashWithdrawal', 'bitcoinCashDepositAddresses', 'bitcoinDepositAddresses', 'bitcoinWithdrawal', 'bitcoinWithdrawalFees', 'buyInstant', 'buyLimit', 'cancelOrder', 'cancelOrderWithInfo', 'createVoucher', 'dashDepositAddresses', 'dashWithdrawal', 'ethereumWithdrawal', 'ethereumDepositAddresses', 'litecoinWithdrawal', 'litecoinDepositAddresses', 'openOrders', 'order', 'orderHistory', 'orderById', 'pusherAuth', 'redeemVoucher', 'replaceByBuyLimit', 'replaceByBuyInstant', 'replaceBySellLimit', 'replaceBySellInstant', 'rippleDepositAddresses', 'rippleWithdrawal', 'sellInstant', 'sellLimit', 'transactionHistory', 'traderFees', 'tradeHistory', 'transfer', 'transferHistory', 'unconfirmedBitcoinDeposits', 'unconfirmedBitcoinCashDeposits', 'unconfirmedDashDeposits', 'unconfirmedEthereumDeposits', 'unconfirmedLitecoinDeposits', 'unconfirmedRippleDeposits', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'maker': 0.12 / 100, 'taker': 0.25 / 100, 'tiers': { 'taker': [ [0, 0.25 / 100], [10000, 0.23 / 100], [100000, 0.21 / 100], [250000, 0.20 / 100], [500000, 0.15 / 100], [1000000, 0.13 / 100], [3000000, 0.10 / 100], [15000000, 0.05 / 100], ], 'maker': [ [0, 0.12 / 100], [10000, 0.11 / 100], [1000000, 0.10 / 100], [250000, 0.08 / 100], [500000, 0.05 / 100], [1000000, 0.03 / 100], [3000000, 0.02 / 100], [15000000, 0], ], }, }, 'promotional': { 'trading': { 'maker': 0.05 / 100, 'taker': 0.15 / 100, 'tiers': { 'taker': [ [0, 0.15 / 100], [10000, 0.14 / 100], [100000, 0.13 / 100], [250000, 0.12 / 100], [500000, 0.11 / 100], [1000000, 0.1 / 100], [3000000, 0.08 / 100], [15000000, 0.05 / 100], ], 'maker': [ [0, 0.05 / 100], [10000, 0.04 / 100], [1000000, 0.03 / 100], [250000, 0.02 / 100], [500000, 0], [1000000, 0], [3000000, 0], [15000000, 0], ], }, }, }, }, 'options': { 'promotionalMarkets': ['ETH/EUR', 'ETH/CZK', 'ETH/BTC', 'XRP/EUR', 'XRP/CZK', 'XRP/BTC', 'DASH/EUR', 'DASH/CZK', 'DASH/BTC', 'BCH/EUR', 'BCH/CZK', 'BCH/BTC'], }, 'exceptions': { 'exact': { 'No order with given ID': OrderNotFound, }, 'broad': { 'Not enough account balance available': InsufficientFunds, 'Incorrect order ID': InvalidOrder, 'Minimum Order Size ': InvalidOrder, 'TOO MANY REQUESTS': RateLimitExceeded, }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetTradingPairs (params); // // { // "error":false, // "errorMessage":null, // "data": [ // { // "name":"BTC_EUR", // "firstCurrency":"BTC", // "secondCurrency":"EUR", // "priceDecimals":2, // "lotDecimals":8, // "minAmount":0.0002, // "tradesWebSocketChannelId":"trades-BTC_EUR", // "orderBookWebSocketChannelId":"order_book-BTC_EUR", // "tradeStatisticsWebSocketChannelId":"statistics-BTC_EUR" // }, // ] // } // const data = this.safeValue (response, 'data'); const result = []; for (let i = 0; i < data.length; i++) { const market = data[i]; const id = this.safeString (market, 'name'); const baseId = this.safeString (market, 'firstCurrency'); const quoteId = this.safeString (market, 'secondCurrency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const promotionalMarkets = this.safeValue (this.options, 'promotionalMarkets', []); let fees = this.safeValue (this.fees, 'trading'); if (this.inArray (symbol, promotionalMarkets)) { const promotionalFees = this.safeValue (this.fees, 'promotional', {}); fees = this.safeValue (promotionalFees, 'trading', fees); } result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': undefined, 'maker': fees['maker'], 'taker': fees['taker'], 'info': market, 'precision': { 'price': this.safeInteger (market, 'priceDecimals'), 'amount': this.safeInteger (market, 'lotDecimals'), }, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minAmount'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostBalances (params); const balances = this.safeValue (response, 'data'); const result = { 'info': response }; const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const balance = this.safeValue (balances, currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'reserved'); account['total'] = this.safeFloat (balance, 'balance'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'currencyPair': this.marketId (symbol), 'groupByPriceLimit': 'False', }; const response = await this.publicGetOrderBook (this.extend (request, params)); const orderbook = response['data']; const timestamp = this.safeTimestamp (orderbook, 'timestamp'); return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'currencyPair': this.marketId (symbol), }; const response = await this.publicGetTicker (this.extend (request, params)); const ticker = this.safeValue (response, 'data'); const timestamp = this.safeTimestamp (ticker, 'timestamp'); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'vwap': undefined, 'askVolume': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'amount'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'limit': 1000, }; if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['timestampFrom'] = since; } if (code !== undefined) { request['currency'] = this.currencyId (code); } const response = await this.privatePostTransferHistory (this.extend (request, params)); const items = response['data']; return this.parseTransactions (items, undefined, since, limit); } parseTransactionStatus (status) { const statuses = { // any other types ? 'COMPLETED': 'ok', }; return this.safeString (statuses, status, status); } parseTransaction (item, currency = undefined) { // // deposits // // { // transactionId: 1862815, // timestamp: 1516803982388, // amountCurrency: 'LTC', // amount: 1, // fee: 0, // walletType: 'LTC', // transferType: 'DEPOSIT', // transferStatus: 'COMPLETED', // txid: // 'ccb9255dfa874e6c28f1a64179769164025329d65e5201849c2400abd6bce245', // destination: 'LQrtSKA6LnhcwRrEuiborQJnjFF56xqsFn', // destinationTag: null // } // // withdrawals // // { // transactionId: 2140966, // timestamp: 1519314282976, // amountCurrency: 'EUR', // amount: 8421.7228, // fee: 16.8772, // walletType: 'BANK_WIRE', // transferType: 'WITHDRAWAL', // transferStatus: 'COMPLETED', // txid: null, // destination: null, // destinationTag: null // } // const timestamp = this.safeInteger (item, 'timestamp'); const amount = this.safeFloat (item, 'amount'); const fee = this.safeFloat (item, 'fee'); const txid = this.safeString (item, 'txid'); const address = this.safeString (item, 'destination'); const tag = this.safeString (item, 'destinationTag'); const currencyId = this.safeString (item, 'amountCurrency'); const code = this.safeCurrencyCode (currencyId, currency); const type = this.safeStringLower (item, 'transferType'); const status = this.parseTransactionStatus (this.safeString (item, 'transferStatus')); const id = this.safeString (item, 'transactionId'); return { 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'currency': code, 'amount': amount, 'type': type, 'txid': txid, 'address': address, 'tag': tag, 'status': status, 'fee': { 'cost': fee, 'currency': currency, }, 'info': item, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (limit === undefined) { limit = 1000; } const request = { 'limit': limit, }; if (symbol !== undefined) { const market = this.market (symbol); request['currencyPair'] = market['id']; } if (since !== undefined) { request['timestampFrom'] = since; } const response = await this.privatePostTradeHistory (this.extend (request, params)); const items = response['data']; return this.parseTrades (items, undefined, since, limit); } parseTrade (trade, market = undefined) { // // fetchMyTrades (private) // // { // transactionId: 2671819, // createdTimestamp: 1529649127605, // currencyPair: 'LTC_BTC', // type: 'BUY', // orderType: 'LIMIT', // orderId: 101810227, // amount: 0.01, // price: 0.01406, // fee: 0, // feeType: 'MAKER' // } // // fetchTrades (public) // // { // "timestamp":1561598833416, // "transactionId":"4156303", // "price":10950.41, // "amount":0.004, // "currencyPair":"BTC_EUR", // "tradeType":"BUY" // } // let symbol = undefined; const marketId = this.safeString (trade, 'currencyPair'); let quote = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id[marketId]) { market = this.markets_by_id[marketId]; quote = market['quote']; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } const side = this.safeStringLower2 (trade, 'type', 'tradeType'); const type = this.safeStringLower (trade, 'orderType'); const orderId = this.safeString (trade, 'orderId'); const id = this.safeString (trade, 'transactionId'); const timestamp = this.safeInteger2 (trade, 'timestamp', 'createdTimestamp'); let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': quote, }; } let takerOrMaker = this.safeString (trade, 'feeType'); takerOrMaker = (takerOrMaker === 'MAKER') ? 'maker' : 'taker'; return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'order': orderId, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPair': market['id'], 'minutesIntoHistory': 10, }; const response = await this.publicGetTransactions (this.extend (request, params)); // // { // "error":false, // "errorMessage":null, // "data":[ // { // "timestamp":1561598833416, // "transactionId":"4156303", // "price":10950.41, // "amount":0.004, // "currencyPair":"BTC_EUR", // "tradeType":"BUY" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTrades (data, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const response = await this.privatePostOpenOrders (this.extend ({}, params)); const extension = { 'status': 'open' }; return this.parseOrders (response['data'], undefined, since, limit, extension); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPair': market['id'], }; // offset param that appears in other parts of the API doesn't appear to be supported here if (limit !== undefined) { request['limit'] = limit; } const response = await this.privatePostOrderHistory (this.extend (request, params)); return this.parseOrders (response['data'], market, since, limit); } parseOrderStatus (status) { const statuses = { 'FILLED': 'closed', 'CANCELLED': 'canceled', 'PARTIALLY_FILLED': 'open', 'OPEN': 'open', }; return this.safeString (statuses, status, status); } parseOrderType (type) { const types = { 'LIMIT': 'limit', 'MARKET': 'market', }; return this.safeString (types, type, type); } parseOrder (order, market = undefined) { // // limit sell // // { // id: 781246605, // timestamp: 1584480015133, // trailingUpdatedTimestamp: null, // type: 'SELL', // currencyPair: 'ETH_BTC', // price: 0.0345, // amount: 0.01, // stopPrice: null, // originalStopPrice: null, // marketPriceAtLastUpdate: null, // marketPriceAtOrderCreation: null, // orderTradeType: 'LIMIT', // hidden: false, // trailing: false, // clientOrderId: null // } // // limit buy // // { // id: 67527001, // timestamp: 1517931722613, // trailingUpdatedTimestamp: null, // type: 'BUY', // price: 5897.24, // remainingAmount: 0.002367, // originalAmount: 0.1, // stopPrice: null, // originalStopPrice: null, // marketPriceAtLastUpdate: null, // marketPriceAtOrderCreation: null, // status: 'CANCELLED', // orderTradeType: 'LIMIT', // hidden: false, // avgPrice: null, // trailing: false, // } // const id = this.safeString (order, 'id'); const timestamp = this.safeInteger (order, 'timestamp'); const side = this.safeStringLower (order, 'type'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat2 (order, 'originalAmount', 'amount'); const remaining = this.safeFloat (order, 'remainingAmount', amount); let status = this.parseOrderStatus (this.safeString (order, 'status')); const type = this.parseOrderType (this.safeString (order, 'orderTradeType')); let filled = undefined; let cost = undefined; if ((amount !== undefined) && (remaining !== undefined)) { filled = Math.min (amount - remaining, 0); if (remaining === 0) { status = 'closed'; } if (price !== undefined) { cost = filled * price; } } const average = this.safeFloat (order, 'avgPrice'); let symbol = undefined; const marketId = this.safeString (order, 'currencyPair'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const clientOrderId = this.safeString (order, 'clientOrderId'); return { 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'trades': undefined, 'info': order, 'fee': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); let method = 'privatePost' + this.capitalize (side); const request = { 'currencyPair': this.marketId (symbol), }; if (type === 'market') { if (side === 'buy') { request['total'] = this.amountToPrecision (symbol, amount); // amount in fiat } else { request['amount'] = this.amountToPrecision (symbol, amount); // amount in fiat } method += 'Instant'; } else { request['amount'] = this.amountToPrecision (symbol, amount); // amount in crypto request['price'] = this.priceToPrecision (symbol, price); method += this.capitalize (type); } const response = await this[method] (this.extend (request, params)); const id = this.safeString (response, 'data'); return { 'info': response, 'id': id, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; let market = undefined; if (symbol) { market = this.market (symbol); } const response = await this.privatePostOrderById (this.extend (request, params)); const data = this.safeValue (response, 'data'); return this.parseOrder (data, market); } async cancelOrder (id, symbol = undefined, params = {}) { // {"error":false,"errorMessage":null,"data":{"success":true,"remainingAmount":0.01}} const request = { 'orderId': id }; const response = await this.privatePostCancelOrderWithInfo (this.extend (request, params)); return { 'info': response, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const auth = nonce + this.uid + this.apiKey; const signature = this.hmac (this.encode (auth), this.encode (this.secret)); body = this.urlencode (this.extend ({ 'clientId': this.uid, 'nonce': nonce, 'publicKey': this.apiKey, 'signature': signature.toUpperCase (), }, params)); headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response !== undefined) { if ('error' in response) { // {"error":true,"errorMessage":"Minimum Order Size 0.01 ETH","data":null} if (response['error']) { const message = this.safeString (response, 'errorMessage'); const feedback = this.id + ' ' + message; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (this.id + ' ' + this.json (response)); } } } if (code > 400) { if (body) { const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], body, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); throw new ExchangeError (feedback); // unknown message } throw new ExchangeError (this.id + ' ' + body); } } }; },{"./base/Exchange":7,"./base/errors":9}],71:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadSymbol, BadRequest, ExchangeError, ArgumentsRequired, InvalidOrder, OrderNotFound, OnMaintenance } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coinone extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinone', 'name': 'CoinOne', 'countries': [ 'KR' ], // Korea 'rateLimit': 667, 'version': 'v2', 'has': { 'CORS': false, 'createMarketOrder': false, 'fetchTickers': true, 'fetchOrder': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/38003300-adc12fba-323f-11e8-8525-725f53c4a659.jpg', 'api': 'https://api.coinone.co.kr', 'www': 'https://coinone.co.kr', 'doc': 'https://doc.coinone.co.kr', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'api': { 'public': { 'get': [ 'orderbook/', 'trades/', 'ticker/', ], }, 'private': { 'post': [ 'account/btc_deposit_address/', 'account/balance/', 'account/daily_balance/', 'account/user_info/', 'account/virtual_account/', 'order/cancel_all/', 'order/cancel/', 'order/limit_buy/', 'order/limit_sell/', 'order/complete_orders/', 'order/limit_orders/', 'order/order_info/', 'transaction/auth_number/', 'transaction/history/', 'transaction/krw/history/', 'transaction/btc/', 'transaction/coin/', ], }, }, 'markets': { 'BCH/KRW': { 'id': 'bch', 'symbol': 'BCH/KRW', 'base': 'BCH', 'quote': 'KRW', 'baseId': 'bch', 'quoteId': 'krw' }, 'BTC/KRW': { 'id': 'btc', 'symbol': 'BTC/KRW', 'base': 'BTC', 'quote': 'KRW', 'baseId': 'btc', 'quoteId': 'krw' }, 'BTG/KRW': { 'id': 'btg', 'symbol': 'BTG/KRW', 'base': 'BTG', 'quote': 'KRW', 'baseId': 'btg', 'quoteId': 'krw' }, 'ETC/KRW': { 'id': 'etc', 'symbol': 'ETC/KRW', 'base': 'ETC', 'quote': 'KRW', 'baseId': 'etc', 'quoteId': 'krw' }, 'ETH/KRW': { 'id': 'eth', 'symbol': 'ETH/KRW', 'base': 'ETH', 'quote': 'KRW', 'baseId': 'eth', 'quoteId': 'krw' }, 'IOTA/KRW': { 'id': 'iota', 'symbol': 'IOTA/KRW', 'base': 'IOTA', 'quote': 'KRW', 'baseId': 'iota', 'quoteId': 'krw' }, 'LTC/KRW': { 'id': 'ltc', 'symbol': 'LTC/KRW', 'base': 'LTC', 'quote': 'KRW', 'baseId': 'ltc', 'quoteId': 'krw' }, 'OMG/KRW': { 'id': 'omg', 'symbol': 'OMG/KRW', 'base': 'OMG', 'quote': 'KRW', 'baseId': 'omg', 'quoteId': 'krw' }, 'QTUM/KRW': { 'id': 'qtum', 'symbol': 'QTUM/KRW', 'base': 'QTUM', 'quote': 'KRW', 'baseId': 'qtum', 'quoteId': 'krw' }, 'XRP/KRW': { 'id': 'xrp', 'symbol': 'XRP/KRW', 'base': 'XRP', 'quote': 'KRW', 'baseId': 'xrp', 'quoteId': 'krw' }, 'EOS/KRW': { 'id': 'eos', 'symbol': 'EOS/KRW', 'base': 'EOS', 'quote': 'KRW', 'baseId': 'eos', 'quoteId': 'krw' }, 'DATA/KRW': { 'id': 'data', 'symbol': 'DATA/KRW', 'base': 'DATA', 'quote': 'KRW', 'baseId': 'data', 'quoteId': 'krw' }, 'ZIL/KRW': { 'id': 'zil', 'symbol': 'ZIL/KRW', 'base': 'ZIL', 'quote': 'KRW', 'baseId': 'zil', 'quoteId': 'krw' }, 'KNC/KRW': { 'id': 'knc', 'symbol': 'KNC/KRW', 'base': 'KNC', 'quote': 'KRW', 'baseId': 'knc', 'quoteId': 'krw' }, 'ZRX/KRW': { 'id': 'zrx', 'symbol': 'ZRX/KRW', 'base': 'ZRX', 'quote': 'KRW', 'baseId': 'zrx', 'quoteId': 'krw' }, 'LUNA/KRW': { 'id': 'luna', 'symbol': 'LUNA/KRW', 'base': 'LUNA', 'quote': 'KRW', 'baseId': 'luna', 'quoteId': 'krw' }, 'ATOM/KRW': { 'id': 'atom', 'symbol': 'ATOM/KRW', 'base': 'ATOM', 'quote': 'KRW', 'baseId': 'atom', 'quoteId': 'krw' }, 'VNT/KRW': { 'id': 'vnt', 'symbol': 'VNT/KRW', 'base': 'VNT', 'quote': 'KRW', 'baseId': 'vnt', 'quoteId': 'krw' }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': 0.001, 'maker': 0.001, 'tiers': { 'taker': [ [0, 0.001], [100000000, 0.0009], [1000000000, 0.0008], [5000000000, 0.0007], [10000000000, 0.0006], [20000000000, 0.0005], [30000000000, 0.0004], [40000000000, 0.0003], [50000000000, 0.0002], ], 'maker': [ [0, 0.001], [100000000, 0.0008], [1000000000, 0.0006], [5000000000, 0.0004], [10000000000, 0.0002], [20000000000, 0], [30000000000, 0], [40000000000, 0], [50000000000, 0], ], }, }, }, 'exceptions': { '405': OnMaintenance, // {"errorCode":"405","status":"maintenance","result":"error"} '104': OrderNotFound, '108': BadSymbol, // {"errorCode":"108","errorMsg":"Unknown CryptoCurrency","result":"error"} '107': BadRequest, // {"errorCode":"107","errorMsg":"Parameter error","result":"error"} }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostAccountBalance (params); const result = { 'info': response }; const balances = this.omit (response, [ 'errorCode', 'result', 'normalWallets', ]); const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const balance = balances[currencyId]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'avail'); account['total'] = this.safeFloat (balance, 'balance'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], 'format': 'json', }; const response = await this.publicGetOrderbook (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'qty'); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = { 'currency': 'all', 'format': 'json', }; const response = await this.publicGetTicker (this.extend (request, params)); const result = {}; const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; let symbol = id; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; const ticker = response[id]; result[symbol] = this.parseTicker (ticker, market); } } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], 'format': 'json', }; const response = await this.publicGetTicker (this.extend (request, params)); return this.parseTicker (response, market); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); const last = this.safeFloat (ticker, 'last'); const previousClose = this.safeFloat (ticker, 'yesterday_last'); let change = undefined; if (last !== undefined && previousClose !== undefined) { change = previousClose - last; } const symbol = (market !== undefined) ? market['symbol'] : undefined; return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'first'), 'close': last, 'last': last, 'previousClose': previousClose, 'change': change, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'timestamp'); const symbol = (market !== undefined) ? market['symbol'] : undefined; const is_ask = this.safeString (trade, 'is_ask'); let side = undefined; if (is_ask === '1') { side = 'sell'; } else if (is_ask === '0') { side = 'buy'; } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'qty'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } return { 'id': undefined, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'order': undefined, 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], 'period': 'hour', 'format': 'json', }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response['completeOrders'], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type !== 'limit') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const request = { 'price': price, 'currency': this.marketId (symbol), 'qty': amount, }; const method = 'privatePostOrder' + this.capitalize (type) + this.capitalize (side); const response = await this[method] (this.extend (request, params)); let id = this.safeString (response, 'orderId'); if (id !== undefined) { id = id.toUpperCase (); } const timestamp = this.milliseconds (); const cost = price * amount; const order = { 'info': response, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': undefined, 'amount': amount, 'filled': undefined, 'remaining': amount, 'status': 'open', 'fee': undefined, 'clientOrderId': undefined, 'trades': undefined, }; this.orders[id] = order; return order; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); let result = undefined; let market = undefined; if (symbol === undefined) { if (id in this.orders) { market = this.market (this.orders[id]['symbol']); } else { throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument for order ids missing in the .orders cache (the order was created with a different instance of this class or within a different run of this code).'); } } else { market = this.market (symbol); } try { const request = { 'order_id': id, 'currency': market['id'], }; const response = await this.privatePostOrderOrderInfo (this.extend (request, params)); result = this.parseOrder (response); this.orders[id] = result; } catch (e) { if (e instanceof OrderNotFound) { if (id in this.orders) { this.orders[id]['status'] = 'canceled'; result = this.orders[id]; } else { throw e; } } else { throw e; } } return result; } parseOrderStatus (status) { const statuses = { 'live': 'open', 'partially_filled': 'open', 'filled': 'closed', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "index": "0", // "orderId": "68665943-1eb5-4e4b-9d76-845fc54f5489", // "timestamp": "1449037367", // "price": "444000.0", // "qty": "0.3456", // "type": "ask", // "feeRate": "-0.0015" // } // const info = this.safeValue (order, 'info'); const id = this.safeStringUpper (info, 'orderId'); const timestamp = this.safeTimestamp (info, 'timestamp'); const status = this.parseOrderStatus (this.safeString (order, 'status')); let cost = undefined; let side = this.safeString (info, 'type'); if (side.indexOf ('ask') >= 0) { side = 'sell'; } else { side = 'buy'; } const price = this.safeFloat (info, 'price'); const amount = this.safeFloat (info, 'qty'); const remaining = this.safeFloat (info, 'remainQty'); let filled = undefined; if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; } if (price !== undefined) { cost = price * amount; } } const currency = this.safeString (info, 'currency'); const fee = { 'currency': currency, 'cost': this.safeFloat (info, 'fee'), 'rate': this.safeFloat (info, 'feeRate'), }; let symbol = undefined; if (market === undefined) { const marketId = currency.toLowerCase (); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'average': undefined, 'trades': undefined, }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const order = this.safeValue (this.orders, id); let amount = undefined; let price = undefined; let side = undefined; if (order === undefined) { if (symbol === undefined) { // eslint-disable-next-line quotes throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `symbol` argument is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); } price = this.safeFloat (params, 'price'); if (price === undefined) { // eslint-disable-next-line quotes throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `price` parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); } amount = this.safeFloat (params, 'qty'); if (amount === undefined) { // eslint-disable-next-line quotes throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `qty` (amount) parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); } side = this.safeFloat (params, 'is_ask'); if (side === undefined) { // eslint-disable-next-line quotes throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `is_ask` (side) parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); } } else { price = order['price']; amount = order['amount']; side = (order['side'] === 'buy') ? 0 : 1; symbol = order['symbol']; } const request = { 'order_id': id, 'price': price, 'qty': amount, 'is_ask': side, 'currency': this.marketId (symbol), }; this.orders[id]['status'] = 'canceled'; return await this.privatePostOrderCancel (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const request = this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'] + '/'; if (api === 'public') { url += request; if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); url += this.version + '/' + request; const nonce = this.nonce ().toString (); const json = this.json (this.extend ({ 'access_token': this.apiKey, 'nonce': nonce, }, params)); const payload = this.stringToBase64 (this.encode (json)); body = this.decode (payload); const secret = this.secret.toUpperCase (); const signature = this.hmac (payload, this.encode (secret), 'sha512'); headers = { 'content-type': 'application/json', 'X-COINONE-PAYLOAD': payload, 'X-COINONE-SIGNATURE': signature, }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } if ('result' in response) { const result = response['result']; if (result !== 'success') { // // { "errorCode": "405", "status": "maintenance", "result": "error"} // const errorCode = this.safeString (response, 'errorCode'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); throw new ExchangeError (feedback); } } else { throw new ExchangeError (this.id + ' ' + body); } } }; },{"./base/Exchange":7,"./base/errors":9}],72:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, NotSupported } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coinspot extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coinspot', 'name': 'CoinSpot', 'countries': [ 'AU' ], // Australia 'rateLimit': 1000, 'has': { 'CORS': false, 'createMarketOrder': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/28208429-3cacdf9a-6896-11e7-854e-4c79a772a30f.jpg', 'api': { 'public': 'https://www.coinspot.com.au/pubapi', 'private': 'https://www.coinspot.com.au/api', }, 'www': 'https://www.coinspot.com.au', 'doc': 'https://www.coinspot.com.au/api', 'referral': 'https://www.coinspot.com.au/register?code=PJURCU', }, 'api': { 'public': { 'get': [ 'latest', ], }, 'private': { 'post': [ 'orders', 'orders/history', 'my/coin/deposit', 'my/coin/send', 'quote/buy', 'quote/sell', 'my/balances', 'my/orders', 'my/buy', 'my/sell', 'my/buy/cancel', 'my/sell/cancel', ], }, }, 'markets': { 'BTC/AUD': { 'id': 'btc', 'symbol': 'BTC/AUD', 'base': 'BTC', 'quote': 'AUD', 'baseId': 'btc', 'quoteId': 'aud' }, 'LTC/AUD': { 'id': 'ltc', 'symbol': 'LTC/AUD', 'base': 'LTC', 'quote': 'AUD', 'baseId': 'ltc', 'quoteId': 'aud' }, 'DOGE/AUD': { 'id': 'doge', 'symbol': 'DOGE/AUD', 'base': 'DOGE', 'quote': 'AUD', 'baseId': 'doge', 'quoteId': 'aud' }, }, 'commonCurrencies': { 'DRK': 'DASH', }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostMyBalances (params); const result = { 'info': response }; const balances = this.safeValue (response, 'balance', {}); const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balances, currencyId); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'cointype': market['id'], }; const orderbook = await this.privatePostOrders (this.extend (request, params)); return this.parseOrderBook (orderbook, undefined, 'buyorders', 'sellorders', 'rate', 'amount'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const response = await this.publicGetLatest (params); let id = this.marketId (symbol); id = id.toLowerCase (); const ticker = response['prices'][id]; const timestamp = this.milliseconds (); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': undefined, 'info': ticker, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'cointype': market['id'], }; const response = await this.privatePostOrdersHistory (this.extend (request, params)); const trades = this.safeValue (response, 'orders', []); return this.parseTrades (trades, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const method = 'privatePostMy' + this.capitalize (side); if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } const request = { 'cointype': this.marketId (symbol), 'amount': amount, 'rate': price, }; return await this[method] (this.extend (request, params)); } async cancelOrder (id, symbol = undefined, params = {}) { throw new NotSupported (this.id + ' cancelOrder () is not fully implemented yet'); // let method = 'privatePostMyBuy'; // return await this[method] ({ 'id': id }); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { if (!this.apiKey) { throw new AuthenticationError (this.id + ' requires apiKey for all requests'); } const url = this.urls['api'][api] + '/' + path; if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.json (this.extend ({ 'nonce': nonce }, params)); headers = { 'Content-Type': 'application/json', 'key': this.apiKey, 'sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],73:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const coinegg = require ('./coinegg.js'); // --------------------------------------------------------------------------- module.exports = class coolcoin extends coinegg { describe () { return this.deepExtend (super.describe (), { 'id': 'coolcoin', 'name': 'CoolCoin', 'countries': [ 'HK' ], 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/36770529-be7b1a04-1c5b-11e8-9600-d11f1996b539.jpg', 'api': { 'web': 'https://www.coolcoin.com/coin', 'rest': 'https://www.coolcoin.com/api/v1', }, 'www': 'https://www.coolcoin.com', 'doc': 'https://www.coolcoin.com/help.api.html', 'fees': 'https://www.coolcoin.com/spend.price.html', 'referral': 'https://www.coolcoin.com/user/register?invite_code=bhaega', }, 'fees': { 'trading': { 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, 'funding': { 'withdraw': { 'BTC': 0.001, 'BCH': 0.002, 'ETH': 0.01, 'ETC': 0.01, 'LTC': 0.001, 'TBC': '1%', 'HSR': '1%', 'NEO': '1%', 'SDC': '1%', 'EOS': '1%', 'BTM': '1%', 'XAS': '1%', 'ACT': '1%', 'SAK': '1%', 'GCS': '1%', 'HCC': '1%', 'QTUM': '1%', 'GEC': '1%', 'TRX': '1%', 'IFC': '1%', 'PAY': '1%', 'PGC': '1%', 'KTC': '1%', 'INT': '1%', 'LSK': '0.5%', 'SKT': '1%', 'SSS': '1%', 'BT1': '1%', 'BT2': '1%', }, }, }, 'options': { 'quoteIds': ['btc', 'usdt'], }, }); } }; },{"./coinegg.js":64}],74:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class coss extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'coss', 'name': 'COSS', 'countries': [ 'SG', 'NL' ], 'rateLimit': 1000, 'version': 'v1', 'certified': false, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/50328158-22e53c00-0503-11e9-825c-c5cfd79bfa74.jpg', 'api': { 'trade': 'https://trade.coss.io/c/api/v1', 'engine': 'https://engine.coss.io/api/v1', 'public': 'https://trade.coss.io/c/api/v1', 'web': 'https://trade.coss.io/c', // undocumented 'exchange': 'https://exchange.coss.io/api', }, 'www': 'https://www.coss.io', 'doc': 'https://api.coss.io/v1/spec', 'referral': 'https://www.coss.io/c/reg?r=OWCMHQVW2Q', }, 'has': { 'fetchTrades': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchMarkets': true, 'fetchCurrencies': true, 'fetchBalance': true, 'fetchOrderBook': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOrderTrades': true, 'fetchClosedOrders': true, 'fetchOpenOrders': true, 'fetchOHLCV': true, 'createOrder': true, 'cancelOrder': true, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '12h': '12h', '1d': '1d', '1w': '1w', }, 'api': { 'exchange': { 'get': [ 'getmarketsummaries', ], }, 'public': { 'get': [ 'market-price', 'exchange-info', ], }, 'web': { 'get': [ 'coins/getinfo/all', // undocumented 'order/symbols', // undocumented 'coins/get_base_list', // undocumented ], }, 'engine': { 'get': [ 'dp', 'ht', 'cs', ], }, 'trade': { 'get': [ 'ping', 'time', 'account/balances', 'account/details', ], 'post': [ 'order/add', 'order/details', 'order/list/open', 'order/list/completed', 'order/list/all', 'order/trade-detail', ], 'delete': [ 'order/cancel', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': 0.0025, 'maker': 0.0, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, 'commonCurrencies': { 'COS': 'COSS', 'COSS': 'COSS.io', }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetExchangeInfo (params); // // { timezone: "UTC", // server_time: 1545171487108, // rate_limits: [ { type: "REQUESTS", // interval: "MINUTE", // limit: 1000 } ], // base_currencies: [ { currency_code: "BTC", minimum_total_order: "0.0001" }, // { currency_code: "USDT", minimum_total_order: "1" }, // { currency_code: "EUR", minimum_total_order: "1" } ], // coins: [ { currency_code: "ADI", // name: "Aditus", // minimum_order_amount: "0.00000001" }, // ... // { currency_code: "NPXSXEM", // name: "PundiX-XEM", // minimum_order_amount: "0.00000001" } ], // symbols: [ { symbol: "ADI_BTC", // amount_limit_decimal: 0, // price_limit_decimal: 8, // allow_trading: true }, // ... // { symbol: "ETH_GUSD", // amount_limit_decimal: 5, // price_limit_decimal: 3, // allow_trading: true } ] } // const result = []; const markets = this.safeValue (response, 'symbols', []); const baseCurrencies = this.safeValue (response, 'base_currencies', []); const baseCurrenciesByIds = this.indexBy (baseCurrencies, 'currency_code'); const currencies = this.safeValue (response, 'coins', []); const currenciesByIds = this.indexBy (currencies, 'currency_code'); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const marketId = market['symbol']; const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'amount_limit_decimal'), 'price': this.safeInteger (market, 'price_limit_decimal'), }; const active = this.safeValue (market, 'allow_trading', false); const baseCurrency = this.safeValue (baseCurrenciesByIds, baseId, {}); const minCost = this.safeFloat (baseCurrency, 'minimum_total_order'); const currency = this.safeValue (currenciesByIds, baseId, {}); const defaultMinAmount = Math.pow (10, -precision['amount']); const minAmount = this.safeFloat (currency, 'minimum_order_amount', defaultMinAmount); result.push ({ 'symbol': symbol, 'id': marketId, 'baseId': baseId, 'quoteId': quoteId, 'base': base, 'quote': quote, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': minAmount, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': minCost, 'max': undefined, }, }, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.webGetCoinsGetinfoAll (params); // // [ { currency_code: "VET", // name: "VeChain", // buy_limit: 0, // sell_limit: 0, // usdt: 0, // transaction_time_limit: 5, // status: "trade", // withdrawn_fee: "0.6", // minimum_withdrawn_amount: "1.2", // minimum_deposit_amount: "0.6", // minimum_order_amount: "0.00000001", // decimal_format: "0.########", // token_type: null, // "erc", "eos", "stellar", "tron", "ripple"... // buy_at: 0, // sell_at: 0, // min_rate: 0, // max_rate: 0, // allow_withdrawn: false, // allow_deposit: false, // explorer_website_mainnet_link: null, // explorer_website_testnet_link: null, // deposit_block_confirmation: "6", // withdraw_block_confirmation: "0", // icon_url: "https://s2.coinmarketcap.com/static/img/coins/32x32/3077.png", // is_fiat: false, // allow_sell: true, // allow_buy: true }] // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const currencyId = this.safeString (currency, 'currency_code'); const code = this.safeCurrencyCode (currencyId); const name = this.safeString (currency, 'name'); const allowBuy = this.safeValue (currency, 'allow_buy'); const allowSell = this.safeValue (currency, 'allow_sell'); const allowWithdrawals = this.safeValue (currency, 'allow_withdrawn'); const allowDeposits = this.safeValue (currency, 'allow_deposit'); const active = allowBuy && allowSell && allowWithdrawals && allowDeposits; const fee = this.safeFloat (currency, 'withdrawn_fee'); const type = this.safeString (currency, 'token_type'); // // decimal_format can be anything... // // 0.######## // #.######## // 0.## // '' (empty string) // 0.000000 // null (undefined) // 0.0000 // 0.### // const decimalFormat = this.safeString (currency, 'decimal_format'); let precision = 8; if (decimalFormat !== undefined) { const parts = decimalFormat.split ('.'); const numParts = parts.length; // transpiler workaround for array lengths if (numParts > 1) { if (parts[1].length > 1) { precision = parts[1].length; } } } result[code] = { 'id': currencyId, 'code': code, 'info': currency, 'name': name, 'active': active, 'fee': fee, 'precision': precision, 'type': type, 'limits': { 'amount': { 'min': this.safeFloat (currency, 'minimum_order_amount'), 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'minimum_withdrawn_amount'), 'max': undefined, }, }, }; } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.tradeGetAccountBalances (params); // // [ { currency_code: "ETH", // address: "0x6820511d43111a941d3e187b9e36ec64af763bde", // deposit address // total: "0.20399125", // available: "0.20399125", // in_order: "0", // memo: null }, // tag, if any // { currency_code: "ICX", // address: "", // total: "0", // available: "0", // in_order: "0", // memo: null } ] // const result = {}; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency_code'); const code = this.safeCurrencyCode (currencyId); const total = this.safeFloat (balance, 'total'); const used = this.safeFloat (balance, 'in_order'); const free = this.safeFloat (balance, 'available'); result[code] = { 'total': total, 'used': used, 'free': free, }; } return this.parseBalance (result); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ parseInt (ohlcv[0]), // timestamp parseFloat (ohlcv[1]), // Open parseFloat (ohlcv[2]), // High parseFloat (ohlcv[3]), // Low parseFloat (ohlcv[4]), // Close parseFloat (ohlcv[5]), // base Volume ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'tt': this.timeframes[timeframe], }; const response = await this.engineGetCs (this.extend (request, params)); // // { tt: "1m", // symbol: "ETH_BTC", // nextTime: 1545138960000, // series: [ [ 1545138960000, // "0.02705000", // "0.02705000", // "0.02705000", // "0.02705000", // "0.00000000" ], // ... // [ 1545168900000, // "0.02684000", // "0.02684000", // "0.02684000", // "0.02684000", // "0.00000000" ] ], // limit: 500 } // return this.parseOHLCVs (response['series'], market, timeframe, since, limit); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const marketId = this.marketId (symbol); const request = { 'symbol': marketId }; // limit argument is not supported on COSS's end const response = await this.engineGetDp (this.extend (request, params)); // // { symbol: "COSS_ETH", // asks: [ ["0.00065200", "214.15000000"], // ["0.00065300", "645.45000000"], // ... // ["0.00076400", "380.00000000"], // ["0.00076900", "25.00000000"] ], // limit: 100, // bids: [ ["0.00065100", "666.99000000"], // ["0.00065000", "1171.93000000"], // ... // ["0.00037700", "3300.00000000"], // ["0.00037600", "2010.82000000"] ], // time: 1545180569354 } // const timestamp = this.safeInteger (response, 'time'); return this.parseOrderBook (response, timestamp); } parseTicker (ticker, market = undefined) { // // { MarketName: "COSS-ETH", // High: 0.00066, // Low: 0.000628, // BaseVolume: 131.09652674, // Last: 0.000636, // TimeStamp: "2018-12-19T05:16:41.369Z", // Volume: 206126.6143710692, // Ask: "0.00063600", // Bid: "0.00063400", // PrevDay: 0.000636 } // const timestamp = this.parse8601 (this.safeString (ticker, 'TimeStamp')); let symbol = undefined; let marketId = this.safeString (ticker, 'MarketName'); if (marketId !== undefined) { marketId = marketId.replace ('-', '_'); } market = this.safeValue (this.markets_by_id, marketId, market); if (market === undefined) { if (marketId !== undefined) { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (market !== undefined) { symbol = market['symbol']; } const previous = this.safeFloat (ticker, 'PrevDay'); const last = this.safeFloat (ticker, 'Last'); let change = undefined; let percentage = undefined; if (last !== undefined) { if (previous !== undefined) { change = last - previous; if (previous > 0) { percentage = (change / previous) * 100; } } } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'High'), 'low': this.safeFloat (ticker, 'Low'), 'bid': this.safeFloat (ticker, 'Bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'Ask'), 'askVolume': undefined, 'vwap': undefined, 'open': previous, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'Volume'), 'quoteVolume': this.safeFloat (ticker, 'BaseVolume'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.exchangeGetGetmarketsummaries (params); // // { success: true, // message: "", // result: [ { MarketName: "COSS-ETH", // High: 0.00066, // Low: 0.000628, // BaseVolume: 131.09652674, // Last: 0.000636, // TimeStamp: "2018-12-19T05:16:41.369Z", // Volume: 206126.6143710692, // Ask: "0.00063600", // Bid: "0.00063400", // PrevDay: 0.000636 }, // ... // { MarketName: "XLM-BTC", // High: 0.0000309, // Low: 0.0000309, // BaseVolume: 0, // Last: 0.0000309, // TimeStamp: "2018-12-19T02:00:02.145Z", // Volume: 0, // Ask: "0.00003300", // Bid: "0.00003090", // PrevDay: 0.0000309 } ], // volumes: [ { CoinName: "ETH", Volume: 668.1928095999999 }, // these are overall exchange volumes // { CoinName: "USD", Volume: 9942.58480324 }, // { CoinName: "BTC", Volume: 43.749184570000004 }, // { CoinName: "COSS", Volume: 909909.26644574 }, // { CoinName: "EUR", Volume: 0 }, // { CoinName: "TUSD", Volume: 2613.3395026999997 }, // { CoinName: "USDT", Volume: 1017152.07416519 }, // { CoinName: "GUSD", Volume: 1.80438 }, // { CoinName: "XRP", Volume: 15.95508 }, // { CoinName: "GBP", Volume: 0 }, // { CoinName: "USDC", Volume: 0 } ], // t: 1545196604371 } // const tickers = this.safeValue (response, 'result', []); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = this.parseTicker (tickers[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchTicker (symbol, params = {}) { const tickers = await this.fetchTickers ([ symbol ], params); return tickers[symbol]; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.engineGetHt (this.extend (request, params)); // // { symbol: "COSS_ETH", // limit: 100, // history: [ { id: 481321, // price: "0.00065100", // qty: "272.92000000", // isBuyerMaker: false, // time: 1545180845019 }, // { id: 481322, // price: "0.00065200", // qty: "1.90000000", // isBuyerMaker: true, // time: 1545180847535 }, // ... // { id: 481420, // price: "0.00065300", // qty: "2.00000000", // isBuyerMaker: true, // time: 1545181167702 } ], // time: 1545181171274 } // return this.parseTrades (response['history'], market, since, limit); } parseTradeFee (fee) { if (fee === undefined) { return fee; } const parts = fee.split (' '); const numParts = parts.length; const cost = parts[0]; let code = undefined; if (numParts > 1) { code = this.safeCurrencyCode (parts[1]); } return { 'cost': cost, 'currency': code, }; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { id: 481322, // price: "0.00065200", // qty: "1.90000000", // isBuyerMaker: true, // time: 1545180847535 } // // fetchOrderTrades (private) // // [ { hex_id: null, // symbol: "COSS_ETH", // order_id: "ad6f6b47-3def-4add-a5d5-2549a9df1593", // order_side: "BUY", // price: "0.00065900", // quantity: "10", // fee: "0.00700000 COSS", // additional_fee: "0.00000461 ETH", // total: "0.00659000 ETH", // timestamp: 1545152356075 } ] // const id = this.safeString (trade, 'id'); const timestamp = this.safeInteger (trade, 'time'); const orderId = this.safeString (trade, 'order_id'); const side = this.safeStringLower (trade, 'order_side'); let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); if (marketId !== undefined) { market = this.safeValue (this.markets_by_id, marketId, market); if (market === undefined) { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } else if (market !== undefined) { symbol = market['symbol']; } let cost = undefined; const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat2 (trade, 'qty', 'quantity'); if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } const result = { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; const fee = this.parseTradeFee (this.safeString (trade, 'fee')); if (fee !== undefined) { const additionalFee = this.parseTradeFee (this.safeString (trade, 'additional_fee')); if (additionalFee === undefined) { result['fee'] = fee; } else { result['fees'] = [ fee, additionalFee, ]; } } return result; } async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { // 'from_id': 'b2a2d379-f9b6-418b-9414-cbf8330b20d1', // string (uuid), fetchOrders (all orders) only // 'page': 0, // different pagination in fetchOpenOrders and fetchClosedOrders // 'limit': 50, // optional, max = default = 50 'symbol': market['id'], // required }; if (limit !== undefined) { request['limit'] = limit; // max = default = 50 } const method = 'tradePostOrderList' + type; const response = await this[method] (this.extend (request, params)); // // fetchOrders, fetchClosedOrders // // [ { hex_id: "5c192784330fe51149f556bb", // order_id: "5e46e1b1-93d5-4656-9b43-a5635b08eae9", // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", // order_symbol: "COSS_ETH", // order_side: "BUY", // status: "filled", // createTime: 1545152388019, // type: "limit", // timeMatching: 0, // order_price: "0.00065900", // order_size: "10", // executed: "10", // stop_price: "0.00000000", // avg: "0.00065900", // total: "0.00659000 ETH" } ] // // fetchOpenOrders // // { // "total": 2, // "list": [ // { // "order_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4ty", // "account_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4a6", // "order_symbol": "eth-btc", // "order_side": "BUY", // "status": "OPEN", // "createTime": 1538114348750, // "type": "limit", // "order_price": "0.12345678", // "order_size": "10.12345678", // "executed": "0", // "stop_price": "02.12345678", // "avg": "1.12345678", // "total": "2.12345678" // } // ] // } // // the following code is to handle the above difference in response formats let orders = undefined; if (Array.isArray (response)) { orders = response; } else { orders = this.safeValue (response, 'list', []); } return this.parseOrders (orders, market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByType ('All', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByType ('Completed', symbol, since, limit, params); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByType ('Open', symbol, since, limit, params); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.tradePostOrderDetails (this.extend (request, params)); return this.parseOrder (response); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'order_id': id, }; const response = await this.tradePostOrderTradeDetail (this.extend (request, params)); // // [ { hex_id: null, // symbol: "COSS_ETH", // order_id: "ad6f6b47-3def-4add-a5d5-2549a9df1593", // order_side: "BUY", // price: "0.00065900", // quantity: "10", // fee: "0.00700000 COSS", // additional_fee: "0.00000461 ETH", // total: "0.00659000 ETH", // timestamp: 1545152356075 } ] // return this.parseTrades (response, market, since, limit); } parseOrderStatus (status) { if (status === undefined) { return status; } const statuses = { 'OPEN': 'open', 'CANCELLED': 'canceled', 'FILLED': 'closed', 'PARTIAL_FILL': 'closed', 'CANCELLING': 'open', }; return this.safeString (statuses, status.toUpperCase (), status); } parseOrder (order, market = undefined) { // // { hex_id: "5c192784330fe51149f556bb", // missing in fetchOpenOrders // order_id: "5e46e1b1-93d5-4656-9b43-a5635b08eae9", // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", // order_symbol: "COSS_ETH", // coss-eth in docs // order_side: "BUY", // status: "filled", // createTime: 1545152388019, // type: "limit", // timeMatching: 0, // missing in fetchOpenOrders // order_price: "0.00065900", // order_size: "10", // executed: "10", // stop_price: "0.00000000", // avg: "0.00065900", // total: "0.00659000 ETH" } // const id = this.safeString (order, 'order_id'); let symbol = undefined; let marketId = this.safeString (order, 'order_symbol'); if (marketId === undefined) { if (market !== undefined) { symbol = market['symbol']; } } else { // a minor workaround for lowercase eth-btc symbols marketId = marketId.toUpperCase (); marketId = marketId.replace ('-', '_'); market = this.safeValue (this.markets_by_id, marketId, market); if (market === undefined) { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } else { symbol = market['symbol']; } } const timestamp = this.safeInteger (order, 'createTime'); const status = this.parseOrderStatus (this.safeString (order, 'status')); const price = this.safeFloat (order, 'order_price'); const filled = this.safeFloat (order, 'executed'); const type = this.safeString (order, 'type'); const amount = this.safeFloat (order, 'order_size'); let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = amount - filled; } } const average = this.safeFloat (order, 'avg'); const side = this.safeStringLower (order, 'order_side'); const cost = this.safeFloat (order, 'total'); const fee = undefined; const trades = undefined; return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': trades, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_symbol': market['id'], 'order_size': this.amountToPrecision (symbol, amount), 'order_side': side.toUpperCase (), 'type': type, }; if (price !== undefined) { request['order_price'] = this.priceToPrecision (symbol, price); } const response = await this.tradePostOrderAdd (this.extend (request, params)); // // { // "order_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4ty", // "account_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4a6", // "order_symbol": "eth-btc", // "order_side": "BUY", // "status": "OPEN", // "createTime": 1538114348750, // "type": "limit", // "order_price": "0.12345678", // "order_size": "10.12345678", // "executed": "0", // "stop_price": "02.12345678", // "avg": "1.12345678", // "total": "2.12345678" // } // return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_id': id, 'order_symbol': market['id'], }; const response = await this.tradeDeleteOrderCancel (this.extend (request, params)); // // { order_symbol: "COSS_ETH", // order_id: "30f2d698-39a0-4b9f-a3a6-a179542373bd", // order_size: 0, // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", // timestamp: 1545202728814, // recvWindow: null } // return this.parseOrder (response); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + path; if (api === 'trade') { this.checkRequiredCredentials (); const timestamp = this.nonce (); const query = this.extend ({ 'timestamp': timestamp, // required (int64) // 'recvWindow': 10000, // optional (int32) }, params); let request = undefined; if (method === 'GET') { request = this.urlencode (query); url += '?' + request; } else { request = this.json (query); body = request; } headers = { 'Signature': this.hmac (this.encode (request), this.encode (this.secret)), 'Authorization': this.apiKey, 'X-Requested-With': 'XMLHttpRequest', }; } else { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],75:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, BadRequest, InvalidNonce, RequestTimeout, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, AuthenticationError, BadSymbol } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class crex24 extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'crex24', 'name': 'CREX24', 'countries': [ 'EE' ], // Estonia 'rateLimit': 500, 'version': 'v2', // new metainfo interface 'has': { 'cancelAllOrders': true, 'CORS': false, 'editOrder': true, 'fetchBidsAsks': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFees': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOrderTrades': true, 'fetchTickers': true, 'fetchTradingFee': false, // actually, true, but will be implemented later 'fetchTradingFees': false, // actually, true, but will be implemented later 'fetchTransactions': true, 'fetchWithdrawals': true, 'withdraw': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '4h': '4h', '1d': '1d', '1w': '1w', '1M': '1mo', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/47813922-6f12cc00-dd5d-11e8-97c6-70f957712d47.jpg', 'api': 'https://api.crex24.com', 'www': 'https://crex24.com', 'referral': 'https://crex24.com/?refid=slxsjsjtil8xexl9hksr', 'doc': 'https://docs.crex24.com/trade-api/v2', 'fees': 'https://crex24.com/fees', }, 'api': { 'public': { 'get': [ 'currencies', 'instruments', 'tickers', 'recentTrades', 'orderBook', 'ohlcv', ], }, 'trading': { 'get': [ 'orderStatus', 'orderTrades', 'activeOrders', 'orderHistory', 'tradeHistory', 'tradeFee', // this is in trading API according to their docs, but most likely a typo in their docs 'moneyTransferStatus', ], 'post': [ 'placeOrder', 'modifyOrder', 'cancelOrdersById', 'cancelOrdersByInstrument', 'cancelAllOrders', ], }, 'account': { 'get': [ 'balance', 'depositAddress', 'moneyTransfers', // this is in trading API according to their docs, but most likely a typo in their docs 'moneyTransferStatus', 'previewWithdrawal', ], 'post': [ 'withdraw', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': 0.001, 'maker': -0.0001, }, // should be deleted, these are outdated and inaccurate 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, 'commonCurrencies': { 'YOYO': 'YOYOW', 'BULL': 'BuySell', 'BCC': 'BCH', }, // exchange-specific options 'options': { 'fetchOrdersMethod': 'tradingGetOrderHistory', // or 'tradingGetActiveOrders' 'fetchClosedOrdersMethod': 'tradingGetOrderHistory', // or 'tradingGetActiveOrders' 'fetchTickersMethod': 'publicGetTicker24hr', 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel 'hasAlreadyAuthenticatedSuccessfully': false, 'warnOnFetchOpenOrdersWithoutSymbol': true, 'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision 'newOrderRespType': 'RESULT', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills }, 'exceptions': { 'exact': { "Parameter 'filter' contains invalid value.": BadRequest, // eslint-disable-quotes "Mandatory parameter 'instrument' is missing.": BadRequest, // eslint-disable-quotes "The value of parameter 'till' must be greater than or equal to the value of parameter 'from'.": BadRequest, // eslint-disable-quotes 'Failed to verify request signature.': AuthenticationError, // eslint-disable-quotes "Nonce error. Make sure that the value passed in the 'X-CREX24-API-NONCE' header is greater in each consecutive request than in the previous one for the corresponding API-Key provided in 'X-CREX24-API-KEY' header.": InvalidNonce, 'Market orders are not supported by the instrument currently.': InvalidOrder, "Parameter 'instrument' contains invalid value.": BadSymbol, }, 'broad': { 'API Key': AuthenticationError, // "API Key '9edc48de-d5b0-4248-8e7e-f59ffcd1c7f1' doesn't exist." 'Insufficient funds': InsufficientFunds, // "Insufficient funds: new order requires 10 ETH which is more than the available balance." 'has been delisted.': BadSymbol, // {"errorDescription":"Instrument '$PAC-BTC' has been delisted."} }, }, }); } nonce () { return this.milliseconds (); } async fetchMarkets (params = {}) { const response = await this.publicGetInstruments (params); // // [ { symbol: "$PAC-BTC", // baseCurrency: "$PAC", // quoteCurrency: "BTC", // feeCurrency: "BTC", // tickSize: 1e-8, // minPrice: 1e-8, // minVolume: 1, // supportedOrderTypes: ["limit"], // state: "active" }, // { symbol: "ZZC-USD", // baseCurrency: "ZZC", // quoteCurrency: "USD", // feeCurrency: "USD", // tickSize: 0.0001, // minPrice: 0.0001, // minVolume: 1, // supportedOrderTypes: ["limit"], // state: "active" } ] // const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'symbol'); const baseId = this.safeString (market, 'baseCurrency'); const quoteId = this.safeString (market, 'quoteCurrency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const tickSize = this.safeValue (market, 'tickSize'); const minPrice = this.safeValue (market, 'minPrice'); const minAmount = this.safeFloat (market, 'minVolume'); const precision = { 'amount': this.precisionFromString (this.numberToString (minAmount)), 'price': this.precisionFromString (this.numberToString (tickSize)), }; const active = (market['state'] === 'active'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': minAmount, 'max': undefined, }, 'price': { 'min': minPrice, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencies (params); // // [ { symbol: "$PAC", // name: "PACCoin", // isFiat: false, // depositsAllowed: true, // depositConfirmationCount: 8, // minDeposit: 0, // withdrawalsAllowed: true, // withdrawalPrecision: 8, // minWithdrawal: 4, // maxWithdrawal: 1000000000, // flatWithdrawalFee: 2, // isDelisted: false }, // { symbol: "ZZC", // name: "Zozo", // isFiat: false, // depositsAllowed: false, // depositConfirmationCount: 8, // minDeposit: 0, // withdrawalsAllowed: false, // withdrawalPrecision: 8, // minWithdrawal: 0.2, // maxWithdrawal: 1000000000, // flatWithdrawalFee: 0.1, // isDelisted: false } ] // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'symbol'); const code = this.safeCurrencyCode (id); const precision = this.safeInteger (currency, 'withdrawalPrecision'); const address = this.safeValue (currency, 'BaseAddress'); const active = (currency['depositsAllowed'] && currency['withdrawalsAllowed'] && !currency['isDelisted']); const type = currency['isFiat'] ? 'fiat' : 'crypto'; result[code] = { 'id': id, 'code': code, 'address': address, 'info': currency, 'type': type, 'name': this.safeString (currency, 'name'), 'active': active, 'fee': this.safeFloat (currency, 'flatWithdrawalFee'), // todo: redesign 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': this.safeFloat (currency, 'minDeposit'), 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'minWithdrawal'), 'max': this.safeFloat (currency, 'maxWithdrawal'), }, }, }; } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const request = { // 'currency': 'ETH', // comma-separated list of currency ids // 'nonZeroOnly': 'false', // true by default }; const response = await this.accountGetBalance (this.extend (request, params)); // // [ // { // "currency": "ETH", // "available": 0.0, // "reserved": 0.0 // } // ] // const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'reserved'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = maximum = 100 } const response = await this.publicGetOrderBook (this.extend (request, params)); // // { buyLevels: [ { price: 0.03099, volume: 0.00610063 }, // { price: 0.03097, volume: 1.33455158 }, // { price: 0.03096, volume: 0.0830889 }, // { price: 0.03095, volume: 0.0820356 }, // { price: 0.03093, volume: 0.5499419 }, // { price: 0.03092, volume: 0.23317494 }, // { price: 0.03091, volume: 0.62105322 }, // { price: 0.00620041, volume: 0.003 } ], // sellLevels: [ { price: 0.03117, volume: 5.47492315 }, // { price: 0.03118, volume: 1.97744139 }, // { price: 0.03119, volume: 0.012 }, // { price: 0.03121, volume: 0.741242 }, // { price: 0.03122, volume: 0.96178089 }, // { price: 0.03123, volume: 0.152326 }, // { price: 0.03124, volume: 2.63462933 }, // { price: 0.069, volume: 0.004 } ] } // return this.parseOrderBook (response, undefined, 'buyLevels', 'sellLevels', 'price', 'volume'); } parseTicker (ticker, market = undefined) { // // { instrument: "ZZC-USD", // last: 0.065, // percentChange: 0, // low: 0.065, // high: 0.065, // baseVolume: 0, // quoteVolume: 0, // volumeInBtc: 0, // volumeInUsd: 0, // ask: 0.5, // bid: 0.0007, // timestamp: "2018-10-31T09:21:25Z" } ] // const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); let symbol = undefined; const marketId = this.safeString (ticker, 'instrument'); market = this.safeValue (this.markets_by_id, marketId, market); if (market !== undefined) { symbol = market['symbol']; } else if (marketId !== undefined) { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, // previous day close 'change': undefined, 'percentage': this.safeFloat (ticker, 'percentChange'), 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'baseVolume'), 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument': market['id'], }; const response = await this.publicGetTickers (this.extend (request, params)); // // [ { instrument: "$PAC-BTC", // last: 3.3e-7, // percentChange: 3.125, // low: 2.7e-7, // high: 3.3e-7, // baseVolume: 191700.79823187, // quoteVolume: 0.0587930939346704, // volumeInBtc: 0.0587930939346704, // volumeInUsd: 376.2006339435353, // ask: 3.3e-7, // bid: 3.1e-7, // timestamp: "2018-10-31T09:21:25Z" } ] // const numTickers = response.length; if (numTickers < 1) { throw new ExchangeError (this.id + ' fetchTicker could not load quotes for symbol ' + symbol); } return this.parseTicker (response[0], market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbols !== undefined) { const ids = this.marketIds (symbols); request['instrument'] = ids.join (','); } const response = await this.publicGetTickers (this.extend (request, params)); // // [ { instrument: "$PAC-BTC", // last: 3.3e-7, // percentChange: 3.125, // low: 2.7e-7, // high: 3.3e-7, // baseVolume: 191700.79823187, // quoteVolume: 0.0587930939346704, // volumeInBtc: 0.0587930939346704, // volumeInUsd: 376.2006339435353, // ask: 3.3e-7, // bid: 3.1e-7, // timestamp: "2018-10-31T09:21:25Z" }, // { instrument: "ZZC-USD", // last: 0.065, // percentChange: 0, // low: 0.065, // high: 0.065, // baseVolume: 0, // quoteVolume: 0, // volumeInBtc: 0, // volumeInUsd: 0, // ask: 0.5, // bid: 0.0007, // timestamp: "2018-10-31T09:21:25Z" } ] // return this.parseTickers (response, symbols); } parseTickers (tickers, symbols = undefined) { const result = []; for (let i = 0; i < tickers.length; i++) { result.push (this.parseTicker (tickers[i])); } return this.filterByArray (result, 'symbol', symbols); } parseTrade (trade, market = undefined) { // // public fetchTrades // // { price: 0.03105, // volume: 0.11, // side: "sell", // timestamp: "2018-10-31T04:19:35Z" } ] // // private fetchMyTrades // // { // "id": 3005866, // "orderId": 468533093, // "timestamp": "2018-06-02T16:26:27Z", // "instrument": "BCH-ETH", // "side": "buy", // "price": 1.78882, // "volume": 0.027, // "fee": 0.0000483, // "feeCurrency": "ETH" // } // const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'volume'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } const id = this.safeString (trade, 'id'); const side = this.safeString (trade, 'side'); const orderId = this.safeString (trade, 'orderId'); let symbol = undefined; const marketId = this.safeString (trade, 'instrument'); market = this.safeValue (this.markets_by_id, marketId, market); if (market !== undefined) { symbol = market['symbol']; } let fee = undefined; const feeCurrencyId = this.safeString (trade, 'feeCurrency'); const feeCode = this.safeCurrencyCode (feeCurrencyId); const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': feeCode, }; } const takerOrMaker = undefined; return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': undefined, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // min 1, max 1000, default 100 } const response = await this.publicGetRecentTrades (this.extend (request, params)); // // [ { price: 0.03117, // volume: 0.02597403, // side: "buy", // timestamp: "2018-10-31T09:37:46Z" }, // { price: 0.03105, // volume: 0.11, // side: "sell", // timestamp: "2018-10-31T04:19:35Z" } ] // return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // { timestamp: '2019-09-21T10:36:00Z', // open: 0.02152, // high: 0.02156, // low: 0.02152, // close: 0.02156, // volume: 0.01741259 } const date = this.safeString (ohlcv, 'timestamp'); const timestamp = this.parse8601 (date); return [ timestamp, this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'granularity': this.timeframes[timeframe], 'instrument': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // Accepted values: 1 - 1000. If the parameter is not specified, the number of results is limited to 100 } const response = await this.publicGetOhlcv (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } parseOrderStatus (status) { const statuses = { 'submitting': 'open', // A newly created limit order has a status "submitting" until it has been processed. // This status changes during the lifetime of an order and can have different values depending on the value of the parameter Time In Force. 'unfilledActive': 'open', // order is active, no trades have been made 'partiallyFilledActive': 'open', // part of the order has been filled, the other part is active 'filled': 'closed', // order has been filled entirely 'partiallyFilledCancelled': 'canceled', // part of the order has been filled, the other part has been cancelled either by the trader or by the system (see the value of cancellationReason of an Order for more details on the reason of cancellation) 'unfilledCancelled': 'canceled', // order has been cancelled, no trades have taken place (see the value of cancellationReason of an Order for more details on the reason of cancellation) }; return (status in statuses) ? statuses[status] : status; } parseOrder (order, market = undefined) { // // createOrder // // { // "id": 469594855, // "timestamp": "2018-06-08T16:59:44Z", // "instrument": "BTS-BTC", // "side": "buy", // "type": "limit", // "status": "submitting", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 4.0, // "price": 0.000025, // "stopPrice": null, // "remainingVolume": 4.0, // "lastUpdate": null, // "parentOrderId": null, // "childOrderId": null // } // const status = this.parseOrderStatus (this.safeString (order, 'status')); let symbol = undefined; const marketId = this.safeString (order, 'instrument'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); let price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'volume'); const remaining = this.safeFloat (order, 'remainingVolume'); let filled = undefined; const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'lastUpdate')); let cost = undefined; if (remaining !== undefined) { if (amount !== undefined) { filled = amount - remaining; if (this.options['parseOrderToPrecision']) { filled = parseFloat (this.amountToPrecision (symbol, filled)); } filled = Math.max (filled, 0.0); if (price !== undefined) { cost = price * filled; } } } const id = this.safeString (order, 'id'); const type = this.safeString (order, 'type'); if (type === 'market') { if (price === 0.0) { if ((cost !== undefined) && (filled !== undefined)) { if ((cost > 0) && (filled > 0)) { price = cost / filled; } } } } const side = this.safeString (order, 'side'); const fee = undefined; const trades = undefined; let average = undefined; if (cost !== undefined) { if (filled) { average = cost / filled; } if (this.options['parseOrderToPrecision']) { cost = parseFloat (this.costToPrecision (symbol, cost)); } } return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': trades, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument': market['id'], 'volume': this.amountToPrecision (symbol, amount), // The value must comply with the list of order types supported by the instrument (see the value of parameter supportedOrderTypes of the Instrument) // If the parameter is not specified, the default value "limit" is used // More about order types in the corresponding section of documentation 'type': type, // 'limit', 'market', 'stopLimit', in fact as of 2018-10-31, only 'limit' orders are supported for all markets 'side': side, // 'buy' or 'sell' // "GTC" - Good-Til-Cancelled // "IOC" - Immediate-Or-Cancel (currently not supported by the exchange API, reserved for future use) // "FOK" - Fill-Or-Kill (currently not supported by the exchange API, reserved for future use) // 'timeInForce': 'GTC', // IOC', 'FOK' // 'strictValidation': false, // false - prices will be rounded to meet the requirement, true - execution of the method will be aborted and an error message will be returned }; let priceIsRequired = false; let stopPriceIsRequired = false; if (type === 'limit') { priceIsRequired = true; } else if (type === 'stopLimit') { priceIsRequired = true; stopPriceIsRequired = true; } if (priceIsRequired) { if (price === undefined) { throw new InvalidOrder (this.id + ' createOrder method requires a price argument for a ' + type + ' order'); } request['price'] = this.priceToPrecision (symbol, price); } if (stopPriceIsRequired) { const stopPrice = this.safeFloat (params, 'stopPrice'); if (stopPrice === undefined) { throw new InvalidOrder (this.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order'); } else { request['stopPrice'] = this.priceToPrecision (symbol, stopPrice); } } const response = await this.tradingPostPlaceOrder (this.extend (request, params)); // // { // "id": 469594855, // "timestamp": "2018-06-08T16:59:44Z", // "instrument": "BTS-BTC", // "side": "buy", // "type": "limit", // "status": "submitting", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 4.0, // "price": 0.000025, // "stopPrice": null, // "remainingVolume": 4.0, // "lastUpdate": null, // "parentOrderId": null, // "childOrderId": null // } // return this.parseOrder (response, market); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.tradingGetOrderStatus (this.extend (request, params)); // // [ // { // "id": 466747915, // "timestamp": "2018-05-26T06:43:49Z", // "instrument": "UNI-BTC", // "side": "sell", // "type": "limit", // "status": "partiallyFilledActive", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 5700.0, // "price": 0.000005, // "stopPrice": null, // "remainingVolume": 1.948051948052, // "lastUpdate": null, // "parentOrderId": null, // "childOrderId": null // } // ] // const numOrders = response.length; if (numOrders < 1) { throw new OrderNotFound (this.id + ' fetchOrder could not fetch order id ' + id); } return this.parseOrder (response[0]); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (since !== undefined) { request['from'] = this.ymdhms (since, 'T'); } if (limit !== undefined) { request['limit'] = limit; } if (symbol !== undefined) { const market = this.market (symbol); request['instrument'] = market['id']; } const method = this.safeString (this.options, 'fetchOrdersMethod', 'tradingGetOrderHistory'); const response = await this[method] (this.extend (request, params)); // // [ // { // "id": 468535711, // "timestamp": "2018-06-02T16:42:40Z", // "instrument": "BTC-EUR", // "side": "sell", // "type": "limit", // "status": "submitting", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 0.00770733, // "price": 6724.9, // "stopPrice": null, // "remainingVolume": 0.00770733, // "lastUpdate": "2018-06-02T16:42:40Z", // "parentOrderId": null, // "childOrderId": null // } // ] // return this.parseOrders (response); } async fetchOrdersByIds (ids = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': ids.join (','), }; const response = await this.tradingGetOrderStatus (this.extend (request, params)); // // [ // { // "id": 466747915, // "timestamp": "2018-05-26T06:43:49Z", // "instrument": "UNI-BTC", // "side": "sell", // "type": "limit", // "status": "partiallyFilledActive", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 5700.0, // "price": 0.000005, // "stopPrice": null, // "remainingVolume": 1.948051948052, // "lastUpdate": null, // "parentOrderId": null, // "childOrderId": null // } // ] // return this.parseOrders (response, undefined, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['instrument'] = market['id']; } const response = await this.tradingGetActiveOrders (this.extend (request, params)); // // [ // { // "id": 466747915, // "timestamp": "2018-05-26T06:43:49Z", // "instrument": "UNI-BTC", // "side": "sell", // "type": "limit", // "status": "partiallyFilledActive", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 5700.0, // "price": 0.000005, // "stopPrice": null, // "remainingVolume": 1.948051948052, // "lastUpdate": null, // "parentOrderId": null, // "childOrderId": null // }, // { // "id": 466748077, // "timestamp": "2018-05-26T06:45:29Z", // "instrument": "PRJ-BTC", // "side": "sell", // "type": "limit", // "status": "partiallyFilledActive", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 10000.0, // "price": 0.0000007, // "stopPrice": null, // "remainingVolume": 9975.0, // "lastUpdate": null, // "parentOrderId": null, // "childOrderId": null // }, // ... // ] // return this.parseOrders (response, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['instrument'] = market['id']; } if (since !== undefined) { request['from'] = this.ymdhms (since, 'T'); } if (limit !== undefined) { request['limit'] = limit; // min 1, max 1000, default 100 } const method = this.safeString (this.options, 'fetchClosedOrdersMethod', 'tradingGetOrderHistory'); const response = await this[method] (this.extend (request, params)); // [ // { // "id": 468535711, // "timestamp": "2018-06-02T16:42:40Z", // "instrument": "BTC-EUR", // "side": "sell", // "type": "limit", // "status": "submitting", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 0.00770733, // "price": 6724.9, // "stopPrice": null, // "remainingVolume": 0.00770733, // "lastUpdate": null, // "parentOrderId": null, // "childOrderId": null // }, // { // "id": 468535707, // "timestamp": "2018-06-02T16:42:37Z", // "instrument": "BTG-BTC", // "side": "buy", // "type": "limit", // "status": "unfilledActive", // "cancellationReason": null, // "timeInForce": "GTC", // "volume": 0.0173737, // "price": 0.00589027, // "stopPrice": null, // "remainingVolume": 0.0173737, // "lastUpdate": null, // "parentOrderId": null, // "childOrderId": null // }, // ... // ] // return this.parseOrders (response, market, since, limit); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'ids': [ parseInt (id), ], }; const response = await this.tradingPostCancelOrdersById (this.extend (request, params)); // // [ // 465448358, // 468364313 // ] // return this.parseOrder (response); } async cancelAllOrders (symbol = undefined, params = {}) { const response = await this.tradingPostCancelAllOrders (params); // // [ // 465448358, // 468364313 // ] // return response; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['instrument'] = market['id']; } if (since !== undefined) { request['from'] = this.ymdhms (since, 'T'); } if (limit !== undefined) { request['limit'] = limit; // min 1, max 1000, default 100 } const response = await this.tradingGetTradeHistory (this.extend (request, params)); // // [ // { // "id": 3005866, // "orderId": 468533093, // "timestamp": "2018-06-02T16:26:27Z", // "instrument": "BCH-ETH", // "side": "buy", // "price": 1.78882, // "volume": 0.027, // "fee": 0.0000483, // "feeCurrency": "ETH" // }, // { // "id": 3005812, // "orderId": 468515771, // "timestamp": "2018-06-02T16:16:05Z", // "instrument": "ETC-BTC", // "side": "sell", // "price": 0.00210958, // "volume": 0.05994006, // "fee": -0.000000063224, // "feeCurrency": "BTC" // }, // ... // ] // return this.parseTrades (response, market, since, limit); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = {}; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (since !== undefined) { request['from'] = this.ymd (since, 'T'); } const response = await this.accountGetMoneyTransfers (this.extend (request, params)); // // [ // { // "id": 756446, // "type": "deposit", // "currency": "ETH", // "address": "0x451d5a1b7519aa75164f440df78c74aac96023fe", // "paymentId": null, // "amount": 0.142, // "fee": null, // "txId": "0x2b49098749840a9482c4894be94f94864b498a1306b6874687a5640cc9871918", // "createdAt": "2018-06-02T19:30:28Z", // "processedAt": "2018-06-02T21:10:41Z", // "confirmationsRequired": 12, // "confirmationCount": 12, // "status": "success", // "errorDescription": null // }, // { // "id": 754618, // "type": "deposit", // "currency": "BTC", // "address": "1IgNfmERVcier4IhfGEfutkLfu4AcmeiUC", // "paymentId": null, // "amount": 0.09, // "fee": null, // "txId": "6876541687a9187e987c9187654f7198b9718af974641687b19a87987f91874f", // "createdAt": "2018-06-02T16:19:44Z", // "processedAt": "2018-06-02T16:20:50Z", // "confirmationsRequired": 1, // "confirmationCount": 1, // "status": "success", // "errorDescription": null // }, // ... // ] // return this.parseTransactions (response, currency, since, limit); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'type': 'deposit', }; return this.fetchTransactions (code, since, limit, this.extend (request, params)); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'type': 'withdrawal', }; return this.fetchTransactions (code, since, limit, this.extend (request, params)); } parseTransactionStatus (status) { const statuses = { 'pending': 'pending', // transfer is in progress 'success': 'ok', // completed successfully 'failed': 'failed', // aborted at some point (money will be credited back to the account of origin) }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // { // "id": 756446, // "type": "deposit", // "currency": "ETH", // "address": "0x451d5a1b7519aa75164f440df78c74aac96023fe", // "paymentId": null, // "amount": 0.142, // "fee": null, // "txId": "0x2b49098749840a9482c4894be94f94864b498a1306b6874687a5640cc9871918", // "createdAt": "2018-06-02T19:30:28Z", // "processedAt": "2018-06-02T21:10:41Z", // "confirmationsRequired": 12, // "confirmationCount": 12, // "status": "success", // "errorDescription": null, // } // const id = this.safeString (transaction, 'id'); const address = this.safeString (transaction, 'address'); const tag = this.safeString (transaction, 'paymentId'); const txid = this.safeValue (transaction, 'txId'); const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const type = this.safeString (transaction, 'type'); const timestamp = this.parse8601 (this.safeString (transaction, 'createdAt')); const updated = this.parse8601 (this.safeString (transaction, 'processedAt')); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const amount = this.safeFloat (transaction, 'amount'); const feeCost = this.safeFloat (transaction, 'fee'); const fee = { 'cost': feeCost, 'currency': code, }; return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': updated, 'fee': fee, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.accountGetDepositAddress (this.extend (request, params)); // // { // "currency": "BTS", // "address": "crex24", // "paymentId": "0fg4da4186741579" // } // const address = this.safeString (response, 'address'); const tag = this.safeString (response, 'paymentId'); return { 'currency': code, 'address': this.checkAddress (address), 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'address': address, 'amount': parseFloat (this.currencyToPrecision (code, amount)), // sets whether the specified amount includes fee, can have either of the two values // true - balance will be decreased by amount, whereas [amount - fee] will be transferred to the specified address // false - amount will be deposited to the specified address, whereas the balance will be decreased by [amount + fee] // 'includeFee': false, // the default value is false }; if (tag !== undefined) { request['paymentId'] = tag; } const response = await this.accountPostWithdraw (this.extend (request, params)); return this.parseTransaction (response); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/' + this.version + '/' + api + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (method === 'GET') { if (Object.keys (query).length) { request += '?' + this.urlencode (query); } } const url = this.urls['api'] + request; if ((api === 'trading') || (api === 'account')) { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const secret = this.base64ToBinary (this.secret); let auth = request + nonce; headers = { 'X-CREX24-API-KEY': this.apiKey, 'X-CREX24-API-NONCE': nonce, }; if (method === 'POST') { headers['Content-Type'] = 'application/json'; body = this.json (params); auth += body; } const signature = this.stringToBase64 (this.hmac (this.encode (auth), secret, 'sha512', 'binary')); headers['X-CREX24-API-SIGN'] = this.decode (signature); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (!this.isJsonEncodedObject (body)) { return; // fallback to default error handler } if ((code >= 200) && (code < 300)) { return; // no error } const message = this.safeString (response, 'errorDescription'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); if (code === 400) { throw new BadRequest (feedback); } else if (code === 401) { throw new AuthenticationError (feedback); } else if (code === 403) { throw new AuthenticationError (feedback); } else if (code === 429) { throw new DDoSProtection (feedback); } else if (code === 500) { throw new ExchangeError (feedback); } else if (code === 503) { throw new ExchangeNotAvailable (feedback); } else if (code === 504) { throw new RequestTimeout (feedback); } throw new ExchangeError (feedback); // unknown message } }; },{"./base/Exchange":7,"./base/errors":9}],76:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { TICK_SIZE } = require ('./base/functions/number'); const { AuthenticationError, ExchangeError, ArgumentsRequired, PermissionDenied, InvalidOrder, OrderNotFound, DDoSProtection, NotSupported, ExchangeNotAvailable, InsufficientFunds, BadRequest, InvalidAddress, OnMaintenance } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class deribit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'deribit', 'name': 'Deribit', 'countries': [ 'NL' ], // Netherlands 'version': 'v2', 'userAgent': undefined, 'rateLimit': 500, 'has': { 'CORS': true, 'editOrder': true, 'fetchBalance': true, 'fetchOrder': true, 'fetchOrders': false, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchTickers': true, 'fetchOHLCV': true, 'fetchDepositAddress': true, 'createDepositAddress': true, 'fetchOrderTrades': true, 'createOrder': true, 'cancelOrder': true, 'cancelAllOrders': true, 'withdraw': true, 'fetchTime': true, 'fetchStatus': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTransactions': false, }, 'timeframes': { '1m': '1', '3m': '3', '5m': '5', '10m': '10', '15m': '15', '30m': '30', '1h': '60', '2h': '120', '3h': '180', '6h': '360', '12h': '720', '1d': '1D', }, 'urls': { 'test': 'https://test.deribit.com', 'logo': 'https://user-images.githubusercontent.com/1294454/41933112-9e2dd65a-798b-11e8-8440-5bab2959fcb8.jpg', 'api': 'https://www.deribit.com', 'www': 'https://www.deribit.com', 'doc': [ 'https://docs.deribit.com/v2', 'https://github.com/deribit', ], 'fees': 'https://www.deribit.com/pages/information/fees', 'referral': 'https://www.deribit.com/reg-1189.4038', }, 'api': { 'public': { 'get': [ // Authentication 'auth', 'exchange_token', 'fork_token', // Session management 'set_heartbeat', 'disable_heartbeat', // Supporting 'get_time', 'hello', 'test', // Subscription management 'subscribe', 'unsubscribe', // Account management 'get_announcements', // Market data 'get_book_summary_by_currency', 'get_book_summary_by_instrument', 'get_contract_size', 'get_currencies', 'get_funding_chart_data', 'get_funding_rate_history', 'get_funding_rate_value', 'get_historical_volatility', 'get_index', 'get_instruments', 'get_last_settlements_by_currency', 'get_last_settlements_by_instrument', 'get_last_trades_by_currency', 'get_last_trades_by_currency_and_time', 'get_last_trades_by_instrument', 'get_last_trades_by_instrument_and_time', 'get_order_book', 'get_trade_volumes', 'get_tradingview_chart_data', 'ticker', ], }, 'private': { 'get': [ // Authentication 'logout', // Session management 'enable_cancel_on_disconnect', 'disable_cancel_on_disconnect', 'get_cancel_on_disconnect', // Subscription management 'subscribe', 'unsubscribe', // Account management 'change_api_key_name', 'change_scope_in_api_key', 'change_subaccount_name', 'create_api_key', 'create_subaccount', 'disable_api_key', 'disable_tfa_for_subaccount', 'enable_api_key', 'get_account_summary', 'get_email_language', 'get_new_announcements', 'get_position', 'get_positions', 'get_subaccounts', 'list_api_keys', 'remove_api_key', 'reset_api_key', 'set_announcement_as_read', 'set_api_key_as_default', 'set_email_for_subaccount', 'set_email_language', 'set_password_for_subaccount', 'toggle_notifications_from_subaccount', 'toggle_subaccount_login', // Block Trade 'execute_block_trade', 'get_block_trade', 'get_last_block_trades_by_currency', 'invalidate_block_trade_signature', 'verify_block_trade', // Trading 'buy', 'sell', 'edit', 'cancel', 'cancel_all', 'cancel_all_by_currency', 'cancel_all_by_instrument', 'cancel_by_label', 'close_position', 'get_margins', 'get_open_orders_by_currency', 'get_open_orders_by_instrument', 'get_order_history_by_currency', 'get_order_history_by_instrument', 'get_order_margin_by_ids', 'get_order_state', 'get_stop_order_history', 'get_user_trades_by_currency', 'get_user_trades_by_currency_and_time', 'get_user_trades_by_instrument', 'get_user_trades_by_instrument_and_time', 'get_user_trades_by_order', 'get_settlement_history_by_instrument', 'get_settlement_history_by_currency', // Wallet 'cancel_transfer_by_id', 'cancel_withdrawal', 'create_deposit_address', 'get_current_deposit_address', 'get_deposits', 'get_transfers', 'get_withdrawals', 'submit_transfer_to_subaccount', 'submit_transfer_to_user', 'withdraw', ], }, }, 'exceptions': { // 0 or absent Success, No error. '9999': PermissionDenied, // 'api_not_enabled' User didn't enable API for the Account. '10000': AuthenticationError, // 'authorization_required' Authorization issue, invalid or absent signature etc. '10001': ExchangeError, // 'error' Some general failure, no public information available. '10002': InvalidOrder, // 'qty_too_low' Order quantity is too low. '10003': InvalidOrder, // 'order_overlap' Rejection, order overlap is found and self-trading is not enabled. '10004': OrderNotFound, // 'order_not_found' Attempt to operate with order that can't be found by specified id. '10005': InvalidOrder, // 'price_too_low ' Price is too low, defines current limit for the operation. '10006': InvalidOrder, // 'price_too_low4idx ' Price is too low for current index, defines current bottom limit for the operation. '10007': InvalidOrder, // 'price_too_high ' Price is too high, defines current up limit for the operation. '10008': InvalidOrder, // 'price_too_high4idx ' Price is too high for current index, defines current up limit for the operation. '10009': InsufficientFunds, // 'not_enough_funds' Account has not enough funds for the operation. '10010': OrderNotFound, // 'already_closed' Attempt of doing something with closed order. '10011': InvalidOrder, // 'price_not_allowed' This price is not allowed for some reason. '10012': InvalidOrder, // 'book_closed' Operation for instrument which order book had been closed. '10013': PermissionDenied, // 'pme_max_total_open_orders ' Total limit of open orders has been exceeded, it is applicable for PME users. '10014': PermissionDenied, // 'pme_max_future_open_orders ' Limit of count of futures' open orders has been exceeded, it is applicable for PME users. '10015': PermissionDenied, // 'pme_max_option_open_orders ' Limit of count of options' open orders has been exceeded, it is applicable for PME users. '10016': PermissionDenied, // 'pme_max_future_open_orders_size ' Limit of size for futures has been exceeded, it is applicable for PME users. '10017': PermissionDenied, // 'pme_max_option_open_orders_size ' Limit of size for options has been exceeded, it is applicable for PME users. '10018': PermissionDenied, // 'non_pme_max_future_position_size ' Limit of size for futures has been exceeded, it is applicable for non-PME users. '10019': PermissionDenied, // 'locked_by_admin' Trading is temporary locked by admin. '10020': ExchangeError, // 'invalid_or_unsupported_instrument' Instrument name is not valid. '10021': InvalidOrder, // 'invalid_amount' Amount is not valid. '10022': InvalidOrder, // 'invalid_quantity' quantity was not recognized as a valid number (for API v1). '10023': InvalidOrder, // 'invalid_price' price was not recognized as a valid number. '10024': InvalidOrder, // 'invalid_max_show' max_show parameter was not recognized as a valid number. '10025': InvalidOrder, // 'invalid_order_id' Order id is missing or its format was not recognized as valid. '10026': InvalidOrder, // 'price_precision_exceeded' Extra precision of the price is not supported. '10027': InvalidOrder, // 'non_integer_contract_amount' Futures contract amount was not recognized as integer. '10028': DDoSProtection, // 'too_many_requests' Allowed request rate has been exceeded. '10029': OrderNotFound, // 'not_owner_of_order' Attempt to operate with not own order. '10030': ExchangeError, // 'must_be_websocket_request' REST request where Websocket is expected. '10031': ExchangeError, // 'invalid_args_for_instrument' Some of arguments are not recognized as valid. '10032': InvalidOrder, // 'whole_cost_too_low' Total cost is too low. '10033': NotSupported, // 'not_implemented' Method is not implemented yet. '10034': InvalidOrder, // 'stop_price_too_high' Stop price is too high. '10035': InvalidOrder, // 'stop_price_too_low' Stop price is too low. '10036': InvalidOrder, // 'invalid_max_show_amount' Max Show Amount is not valid. '10040': ExchangeNotAvailable, // 'retry' Request can't be processed right now and should be retried. '10041': OnMaintenance, // 'settlement_in_progress' Settlement is in progress. Every day at settlement time for several seconds, the system calculates user profits and updates balances. That time trading is paused for several seconds till the calculation is completed. '10043': InvalidOrder, // 'price_wrong_tick' Price has to be rounded to a certain tick size. '10044': InvalidOrder, // 'stop_price_wrong_tick' Stop Price has to be rounded to a certain tick size. '10045': InvalidOrder, // 'can_not_cancel_liquidation_order' Liquidation order can't be canceled. '10046': InvalidOrder, // 'can_not_edit_liquidation_order' Liquidation order can't be edited. '10047': DDoSProtection, // 'matching_engine_queue_full' Reached limit of pending Matching Engine requests for user. '10048': ExchangeError, // 'not_on_this_server' The requested operation is not available on this server. '11008': InvalidOrder, // 'already_filled' This request is not allowed in regards to the filled order. '11029': BadRequest, // 'invalid_arguments' Some invalid input has been detected. '11030': ExchangeError, // 'other_reject ' Some rejects which are not considered as very often, more info may be specified in . '11031': ExchangeError, // 'other_error ' Some errors which are not considered as very often, more info may be specified in . '11035': DDoSProtection, // 'no_more_stops ' Allowed amount of stop orders has been exceeded. '11036': InvalidOrder, // 'invalid_stoppx_for_index_or_last' Invalid StopPx (too high or too low) as to current index or market. '11037': BadRequest, // 'outdated_instrument_for_IV_order' Instrument already not available for trading. '11038': InvalidOrder, // 'no_adv_for_futures' Advanced orders are not available for futures. '11039': InvalidOrder, // 'no_adv_postonly' Advanced post-only orders are not supported yet. '11041': InvalidOrder, // 'not_adv_order' Advanced order properties can't be set if the order is not advanced. '11042': PermissionDenied, // 'permission_denied' Permission for the operation has been denied. '11043': BadRequest, // 'bad_argument' Bad argument has been passed. '11044': InvalidOrder, // 'not_open_order' Attempt to do open order operations with the not open order. '11045': BadRequest, // 'invalid_event' Event name has not been recognized. '11046': BadRequest, // 'outdated_instrument' At several minutes to instrument expiration, corresponding advanced implied volatility orders are not allowed. '11047': BadRequest, // 'unsupported_arg_combination' The specified combination of arguments is not supported. '11048': ExchangeError, // 'wrong_max_show_for_option' Wrong Max Show for options. '11049': BadRequest, // 'bad_arguments' Several bad arguments have been passed. '11050': BadRequest, // 'bad_request' Request has not been parsed properly. '11051': OnMaintenance, // 'system_maintenance' System is under maintenance. '11052': ExchangeError, // 'subscribe_error_unsubscribed' Subscription error. However, subscription may fail without this error, please check list of subscribed channels returned, as some channels can be not subscribed due to wrong input or lack of permissions. '11053': ExchangeError, // 'transfer_not_found' Specified transfer is not found. '11090': InvalidAddress, // 'invalid_addr' Invalid address. '11091': InvalidAddress, // 'invalid_transfer_address' Invalid addres for the transfer. '11092': InvalidAddress, // 'address_already_exist' The address already exists. '11093': DDoSProtection, // 'max_addr_count_exceeded' Limit of allowed addresses has been reached. '11094': ExchangeError, // 'internal_server_error' Some unhandled error on server. Please report to admin. The details of the request will help to locate the problem. '11095': ExchangeError, // 'disabled_deposit_address_creation' Deposit address creation has been disabled by admin. '11096': ExchangeError, // 'address_belongs_to_user' Withdrawal instead of transfer. '12000': AuthenticationError, // 'bad_tfa' Wrong TFA code '12001': DDoSProtection, // 'too_many_subaccounts' Limit of subbacounts is reached. '12002': ExchangeError, // 'wrong_subaccount_name' The input is not allowed as name of subaccount. '12998': AuthenticationError, // 'tfa_over_limit' The number of failed TFA attempts is limited. '12003': AuthenticationError, // 'login_over_limit' The number of failed login attempts is limited. '12004': AuthenticationError, // 'registration_over_limit' The number of registration requests is limited. '12005': AuthenticationError, // 'country_is_banned' The country is banned (possibly via IP check). '12100': ExchangeError, // 'transfer_not_allowed' Transfer is not allowed. Possible wrong direction or other mistake. '12999': AuthenticationError, // 'tfa_used' TFA code is correct but it is already used. Please, use next code. '13000': AuthenticationError, // 'invalid_login' Login name is invalid (not allowed or it contains wrong characters). '13001': AuthenticationError, // 'account_not_activated' Account must be activated. '13002': PermissionDenied, // 'account_blocked' Account is blocked by admin. '13003': AuthenticationError, // 'tfa_required' This action requires TFA authentication. '13004': AuthenticationError, // 'invalid_credentials' Invalid credentials has been used. '13005': AuthenticationError, // 'pwd_match_error' Password confirmation error. '13006': AuthenticationError, // 'security_error' Invalid Security Code. '13007': AuthenticationError, // 'user_not_found' User's security code has been changed or wrong. '13008': ExchangeError, // 'request_failed' Request failed because of invalid input or internal failure. '13009': AuthenticationError, // 'unauthorized' Wrong or expired authorization token or bad signature. For example, please check scope of the token, 'connection' scope can't be reused for other connections. '13010': BadRequest, // 'value_required' Invalid input, missing value. '13011': BadRequest, // 'value_too_short' Input is too short. '13012': PermissionDenied, // 'unavailable_in_subaccount' Subaccount restrictions. '13013': BadRequest, // 'invalid_phone_number' Unsupported or invalid phone number. '13014': BadRequest, // 'cannot_send_sms' SMS sending failed -- phone number is wrong. '13015': BadRequest, // 'invalid_sms_code' Invalid SMS code. '13016': BadRequest, // 'invalid_input' Invalid input. '13017': ExchangeError, // 'subscription_failed' Subscription hailed, invalid subscription parameters. '13018': ExchangeError, // 'invalid_content_type' Invalid content type of the request. '13019': ExchangeError, // 'orderbook_closed' Closed, expired order book. '13020': ExchangeError, // 'not_found' Instrument is not found, invalid instrument name. '13021': PermissionDenied, // 'forbidden' Not enough permissions to execute the request, forbidden. '13025': ExchangeError, // 'method_switched_off_by_admin' API method temporarily switched off by administrator. '-32602': BadRequest, // 'Invalid params' see JSON-RPC spec. '-32601': BadRequest, // 'Method not found' see JSON-RPC spec. '-32700': BadRequest, // 'Parse error' see JSON-RPC spec. '-32000': BadRequest, // 'Missing params' see JSON-RPC spec. }, 'precisionMode': TICK_SIZE, 'options': { 'code': 'BTC', 'fetchBalance': { 'code': 'BTC', }, }, }); } async fetchTime (params = {}) { const response = await this.publicGetGetTime (params); // // { // jsonrpc: '2.0', // result: 1583922446019, // usIn: 1583922446019955, // usOut: 1583922446019956, // usDiff: 1, // testnet: false // } // return this.safeInteger (response, 'result'); } codeFromOptions (methodName) { const defaultCode = this.safeValue (this.options, 'code', 'BTC'); const options = this.safeValue (this.options, methodName, {}); return this.safeValue (options, 'code', defaultCode); } async fetchStatus (params = {}) { const request = { // 'expected_result': false, // true will trigger an error for testing purposes }; await this.publicGetTest (this.extend (request, params)); // // { // jsonrpc: '2.0', // result: { version: '1.2.26' }, // usIn: 1583922623964485, // usOut: 1583922623964487, // usDiff: 2, // testnet: false // } // this.status = this.extend (this.status, { 'status': 'ok', 'updated': this.milliseconds (), }); return this.status; } async fetchMarkets (params = {}) { const currenciesResponse = await this.publicGetGetCurrencies (params); // // { // jsonrpc: '2.0', // result: [ // { // withdrawal_priorities: [ // { value: 0.15, name: 'very_low' }, // { value: 1.5, name: 'very_high' }, // ], // withdrawal_fee: 0.0005, // min_withdrawal_fee: 0.0005, // min_confirmations: 1, // fee_precision: 4, // currency_long: 'Bitcoin', // currency: 'BTC', // coin_type: 'BITCOIN' // } // ], // usIn: 1583761588590479, // usOut: 1583761588590544, // usDiff: 65, // testnet: false // } // const currenciesResult = this.safeValue (currenciesResponse, 'result', []); const result = []; for (let i = 0; i < currenciesResult.length; i++) { const currencyId = this.safeString (currenciesResult[i], 'currency'); const request = { 'currency': currencyId, }; const instrumentsResponse = await this.publicGetGetInstruments (this.extend (request, params)); // // { // jsonrpc: '2.0', // result: [ // { // tick_size: 0.0005, // taker_commission: 0.0004, // strike: 300, // settlement_period: 'week', // quote_currency: 'USD', // option_type: 'call', // min_trade_amount: 1, // maker_commission: 0.0004, // kind: 'option', // is_active: true, // instrument_name: 'ETH-13MAR20-300-C', // expiration_timestamp: 1584086400000, // creation_timestamp: 1582790403000, // contract_size: 1, // base_currency: 'ETH' // }, // ], // usIn: 1583761889500586, // usOut: 1583761889505066, // usDiff: 4480, // testnet: false // } // const instrumentsResult = this.safeValue (instrumentsResponse, 'result', []); for (let k = 0; k < instrumentsResult.length; k++) { const market = instrumentsResult[k]; const id = this.safeString (market, 'instrument_name'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const type = this.safeString (market, 'kind'); const future = (type === 'future'); const option = (type === 'option'); const active = this.safeValue (market, 'is_active'); const minTradeAmount = this.safeFloat (market, 'min_trade_amount'); const tickSize = this.safeFloat (market, 'tick_size'); const precision = { 'amount': minTradeAmount, 'price': tickSize, }; result.push ({ 'id': id, 'symbol': id, 'base': base, 'quote': quote, 'active': active, 'precision': precision, 'taker': this.safeFloat (market, 'taker_commission'), 'maker': this.safeFloat (market, 'maker_commission'), 'limits': { 'amount': { 'min': minTradeAmount, 'max': undefined, }, 'price': { 'min': tickSize, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'type': type, 'spot': false, 'future': future, 'option': option, 'info': market, }); } } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const code = this.codeFromOptions ('fetchBalance'); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privateGetGetAccountSummary (this.extend (request, params)); // // { // jsonrpc: '2.0', // result: { // total_pl: 0, // session_upl: 0, // session_rpl: 0, // session_funding: 0, // portfolio_margining_enabled: false, // options_vega: 0, // options_theta: 0, // options_session_upl: 0, // options_session_rpl: 0, // options_pl: 0, // options_gamma: 0, // options_delta: 0, // margin_balance: 0.00062359, // maintenance_margin: 0, // limits: { // non_matching_engine_burst: 300, // non_matching_engine: 200, // matching_engine_burst: 20, // matching_engine: 2 // }, // initial_margin: 0, // futures_session_upl: 0, // futures_session_rpl: 0, // futures_pl: 0, // equity: 0.00062359, // deposit_address: '13tUtNsJSZa1F5GeCmwBywVrymHpZispzw', // delta_total: 0, // currency: 'BTC', // balance: 0.00062359, // available_withdrawal_funds: 0.00062359, // available_funds: 0.00062359 // }, // usIn: 1583775838115975, // usOut: 1583775838116520, // usDiff: 545, // testnet: false // } // const result = { 'info': response, }; const balance = this.safeValue (response, 'result', {}); const currencyId = this.safeString (balance, 'currency'); const currencyCode = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'availableFunds'); account['used'] = this.safeFloat (balance, 'maintenanceMargin'); account['total'] = this.safeFloat (balance, 'equity'); result[currencyCode] = account; return this.parseBalance (result); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privateGetCreateDepositAddress (this.extend (request, params)); // // { // 'jsonrpc': '2.0', // 'id': 7538, // 'result': { // 'address': '2N8udZGBc1hLRCFsU9kGwMPpmYUwMFTuCwB', // 'creation_timestamp': 1550575165170, // 'currency': 'BTC', // 'type': 'deposit' // } // } // const result = this.safeValue (response, 'result', {}); const address = this.safeString (result, 'address'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': undefined, 'info': response, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privateGetGetCurrentDepositAddress (this.extend (request, params)); // // { // jsonrpc: '2.0', // result: { // type: 'deposit', // status: 'ready', // requires_confirmation: true, // currency: 'BTC', // creation_timestamp: 1514694684651, // address: '13tUtNsJSZa1F5GeCmwBywVrymHpZispzw' // }, // usIn: 1583785137274288, // usOut: 1583785137274454, // usDiff: 166, // testnet: false // } // const result = this.safeValue (response, 'result', {}); const address = this.safeString (result, 'address'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': undefined, 'info': response, }; } parseTicker (ticker, market = undefined) { // // fetchTicker /public/ticker // // { // timestamp: 1583778859480, // stats: { volume: 60627.57263769, low: 7631.5, high: 8311.5 }, // state: 'open', // settlement_price: 7903.21, // open_interest: 111543850, // min_price: 7634, // max_price: 7866.51, // mark_price: 7750.02, // last_price: 7750.5, // instrument_name: 'BTC-PERPETUAL', // index_price: 7748.01, // funding_8h: 0.0000026, // current_funding: 0, // best_bid_price: 7750, // best_bid_amount: 19470, // best_ask_price: 7750.5, // best_ask_amount: 343280 // } // // fetchTicker /public/get_book_summary_by_instrument // fetchTickers /public/get_book_summary_by_currency // // { // volume: 124.1, // underlying_price: 7856.445926872601, // underlying_index: 'SYN.BTC-10MAR20', // quote_currency: 'USD', // open_interest: 121.8, // mid_price: 0.01975, // mark_price: 0.01984559, // low: 0.0095, // last: 0.0205, // interest_rate: 0, // instrument_name: 'BTC-10MAR20-7750-C', // high: 0.0295, // estimated_delivery_price: 7856.29, // creation_timestamp: 1583783678366, // bid_price: 0.0185, // base_currency: 'BTC', // ask_price: 0.021 // }, // const timestamp = this.safeInteger2 (ticker, 'timestamp', 'creation_timestamp'); const marketId = this.safeString (ticker, 'instrument_name'); let symbol = marketId; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const last = this.safeFloat2 (ticker, 'last_price', 'last'); const stats = this.safeValue (ticker, 'stats', ticker); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat2 (stats, 'high', 'max_price'), 'low': this.safeFloat2 (stats, 'low', 'min_price'), 'bid': this.safeFloat2 (ticker, 'best_bid_price', 'bid_price'), 'bidVolume': this.safeFloat (ticker, 'best_bid_amount'), 'ask': this.safeFloat2 (ticker, 'best_ask_price', 'ask_price'), 'askVolume': this.safeFloat (ticker, 'best_ask_amount'), 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeFloat (stats, 'volume'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument_name': market['id'], }; const response = await this.publicGetTicker (this.extend (request, params)); // // { // jsonrpc: '2.0', // result: { // timestamp: 1583778859480, // stats: { volume: 60627.57263769, low: 7631.5, high: 8311.5 }, // state: 'open', // settlement_price: 7903.21, // open_interest: 111543850, // min_price: 7634, // max_price: 7866.51, // mark_price: 7750.02, // last_price: 7750.5, // instrument_name: 'BTC-PERPETUAL', // index_price: 7748.01, // funding_8h: 0.0000026, // current_funding: 0, // best_bid_price: 7750, // best_bid_amount: 19470, // best_ask_price: 7750.5, // best_ask_amount: 343280 // }, // usIn: 1583778859483941, // usOut: 1583778859484075, // usDiff: 134, // testnet: false // } // const result = this.safeValue (response, 'result'); return this.parseTicker (result, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const code = this.codeFromOptions ('fetchTickers'); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.publicGetGetBookSummaryByCurrency (this.extend (request, params)); // // { // jsonrpc: '2.0', // result: [ // { // volume: 124.1, // underlying_price: 7856.445926872601, // underlying_index: 'SYN.BTC-10MAR20', // quote_currency: 'USD', // open_interest: 121.8, // mid_price: 0.01975, // mark_price: 0.01984559, // low: 0.0095, // last: 0.0205, // interest_rate: 0, // instrument_name: 'BTC-10MAR20-7750-C', // high: 0.0295, // estimated_delivery_price: 7856.29, // creation_timestamp: 1583783678366, // bid_price: 0.0185, // base_currency: 'BTC', // ask_price: 0.021 // }, // ], // usIn: 1583783678361966, // usOut: 1583783678372069, // usDiff: 10103, // testnet: false // } // const result = this.safeValue (response, 'result', []); const tickers = {}; for (let i = 0; i < result.length; i++) { const ticker = this.parseTicker (result[i]); const symbol = ticker['symbol']; tickers[symbol] = ticker; } return this.filterByArray (tickers, 'symbol', symbols); } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument_name': market['id'], 'resolution': this.timeframes[timeframe], }; const duration = this.parseTimeframe (timeframe); const now = this.milliseconds (); if (since === undefined) { if (limit === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument or a limit argument'); } else { request['start_timestamp'] = now - (limit - 1) * duration * 1000; request['end_timestamp'] = now; } } else { request['start_timestamp'] = since; if (limit === undefined) { request['end_timestamp'] = now; } else { request['end_timestamp'] = this.sum (since, limit * duration * 1000); } } const response = await this.publicGetGetTradingviewChartData (this.extend (request, params)); // // { // jsonrpc: '2.0', // result: { // volume: [ 3.6680847969999992, 22.682721123, 3.011587939, 0 ], // ticks: [ 1583916960000, 1583917020000, 1583917080000, 1583917140000 ], // status: 'ok', // open: [ 7834, 7839, 7833.5, 7833 ], // low: [ 7834, 7833.5, 7832.5, 7833 ], // high: [ 7839.5, 7839, 7833.5, 7833 ], // cost: [ 28740, 177740, 23590, 0 ], // close: [ 7839.5, 7833.5, 7833, 7833 ] // }, // usIn: 1583917166709801, // usOut: 1583917166710175, // usDiff: 374, // testnet: false // } // const result = this.safeValue (response, 'result', {}); const ohlcvs = this.convertTradingViewToOHLCV (result, 'ticks', 'open', 'high', 'low', 'close', 'volume', true); return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // 'trade_seq': 39201926, // 'trade_id':' 64135724', // 'timestamp': 1583174775400, // 'tick_direction': 1, // 'price': 8865.0, // 'instrument_name': 'BTC-PERPETUAL', // 'index_price': 8863.31, // 'direction': 'buy', // 'amount': 10.0 // } // // fetchMyTrades, fetchOrderTrades (private) // // { // "trade_seq": 3, // "trade_id": "ETH-34066", // "timestamp": 1550219814585, // "tick_direction": 1, // "state": "open", // "self_trade": false, // "reduce_only": false, // "price": 0.04, // "post_only": false, // "order_type": "limit", // "order_id": "ETH-334607", // "matching_id": null, // "liquidity": "M", // "iv": 56.83, // "instrument_name": "ETH-22FEB19-120-C", // "index_price": 121.37, // "fee_currency": "ETH", // "fee": 0.0011, // "direction": "buy", // "amount": 11 // } // const id = this.safeString (trade, 'trade_id'); let symbol = undefined; const marketId = this.safeString (trade, 'instrument_name'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.safeInteger (trade, 'timestamp'); const side = this.safeString (trade, 'direction'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } const liquidity = this.safeString (trade, 'liquidity'); let takerOrMaker = undefined; if (liquidity !== undefined) { // M = maker, T = taker, MT = both takerOrMaker = (liquidity === 'M') ? 'maker' : 'taker'; } const feeCost = this.safeFloat (trade, 'fee'); let fee = undefined; if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'fee_currency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': this.safeString (trade, 'order_id'), 'type': this.safeString (trade, 'order_type'), 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument_name': market['id'], 'include_old': true, }; const method = (since === undefined) ? 'publicGetGetLastTradesByInstrument' : 'publicGetGetLastTradesByInstrumentAndTime'; if (since !== undefined) { request['start_timestamp'] = since; } if (limit !== undefined) { request['count'] = limit; // default 10 } const response = await this[method] (this.extend (request, params)); // // { // 'jsonrpc': '2.0', // 'result': { // 'trades': [ // { // 'trade_seq': 39201926, // 'trade_id':' 64135724', // 'timestamp': 1583174775400, // 'tick_direction': 1, // 'price': 8865.0, // 'instrument_name': 'BTC-PERPETUAL', // 'index_price': 8863.31, // 'direction': 'buy', // 'amount': 10.0 // }, // ], // 'has_more': true, // }, // 'usIn': 1583779594843931, // 'usOut': 1583779594844446, // 'usDiff': 515, // 'testnet': false // } // const result = this.safeValue (response, 'result', {}); const trades = this.safeValue (result, 'trades', []); return this.parseTrades (trades, market, since, limit); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument_name': market['id'], }; if (limit !== undefined) { request['depth'] = limit; } const response = await this.publicGetGetOrderBook (this.extend (request, params)); // // { // jsonrpc: '2.0', // result: { // timestamp: 1583781354740, // stats: { volume: 61249.66735634, low: 7631.5, high: 8311.5 }, // state: 'open', // settlement_price: 7903.21, // open_interest: 111536690, // min_price: 7695.13, // max_price: 7929.49, // mark_price: 7813.06, // last_price: 7814.5, // instrument_name: 'BTC-PERPETUAL', // index_price: 7810.12, // funding_8h: 0.0000031, // current_funding: 0, // change_id: 17538025952, // bids: [ // [7814, 351820], // [7813.5, 207490], // [7813, 32160], // ], // best_bid_price: 7814, // best_bid_amount: 351820, // best_ask_price: 7814.5, // best_ask_amount: 11880, // asks: [ // [7814.5, 11880], // [7815, 18100], // [7815.5, 2640], // ], // }, // usIn: 1583781354745804, // usOut: 1583781354745932, // usDiff: 128, // testnet: false // } // const result = this.safeValue (response, 'result', {}); const timestamp = this.safeInteger (result, 'timestamp'); const nonce = this.safeInteger (result, 'change_id'); const orderbook = this.parseOrderBook (result, timestamp); orderbook['nonce'] = nonce; return orderbook; } parseOrderStatus (status) { const statuses = { 'open': 'open', 'cancelled': 'canceled', 'filled': 'closed', 'rejected': 'rejected', // 'untriggered': 'open', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "time_in_force": "good_til_cancelled", // "reduce_only": false, // "profit_loss": 0, // "price": "market_price", // "post_only": false, // "order_type": "market", // "order_state": "filled", // "order_id": "ETH-349249", // "max_show": 40, // "last_update_timestamp": 1550657341322, // "label": "market0000234", // "is_liquidation": false, // "instrument_name": "ETH-PERPETUAL", // "filled_amount": 40, // "direction": "buy", // "creation_timestamp": 1550657341322, // "commission": 0.000139, // "average_price": 143.81, // "api": true, // "amount": 40, // "trades": [], // injected by createOrder // } // const timestamp = this.safeInteger (order, 'creation_timestamp'); const lastUpdate = this.safeInteger (order, 'last_update_timestamp'); const id = this.safeString (order, 'order_id'); const price = this.safeFloat (order, 'price'); const average = this.safeFloat (order, 'average_price'); const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'filled_amount'); let lastTradeTimestamp = undefined; if (filled !== undefined) { if (filled > 0) { lastTradeTimestamp = lastUpdate; } } let remaining = undefined; let cost = undefined; if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; } if (price !== undefined) { cost = price * filled; } } const status = this.parseOrderStatus (this.safeString (order, 'order_state')); const marketId = this.safeString (order, 'instrument_name'); let symbol = undefined; let base = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; base = market['base']; } if (market !== undefined) { if (symbol === undefined) { symbol = market['symbol']; } if (base === undefined) { base = market['base']; } } const side = this.safeStringLower (order, 'direction'); let feeCost = this.safeFloat (order, 'commission'); let fee = undefined; if (feeCost !== undefined) { feeCost = Math.abs (feeCost); fee = { 'cost': feeCost, 'currency': base, }; } const type = this.safeString (order, 'order_type'); // injected in createOrder let trades = this.safeValue (order, 'trades'); if (trades !== undefined) { trades = this.parseTrades (trades, market); } return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': trades, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privateGetGetOrderState (this.extend (request, params)); // // { // "jsonrpc": "2.0", // "id": 4316, // "result": { // "time_in_force": "good_til_cancelled", // "reduce_only": false, // "profit_loss": 0.051134, // "price": 118.94, // "post_only": false, // "order_type": "limit", // "order_state": "filled", // "order_id": "ETH-331562", // "max_show": 37, // "last_update_timestamp": 1550219810944, // "label": "", // "is_liquidation": false, // "instrument_name": "ETH-PERPETUAL", // "filled_amount": 37, // "direction": "sell", // "creation_timestamp": 1550219749176, // "commission": 0.000031, // "average_price": 118.94, // "api": false, // "amount": 37 // } // } // const result = this.safeValue (response, 'result'); return this.parseOrder (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'instrument_name': market['id'], // for perpetual and futures the amount is in USD // for options it is in corresponding cryptocurrency contracts, e.g., BTC or ETH 'amount': this.amountToPrecision (symbol, amount), 'type': type, // limit, stop_limit, market, stop_market, default is limit // 'label': 'string', // user-defined label for the order (maximum 64 characters) // 'price': this.priceToPrecision (symbol, 123.45), // only for limit and stop_limit orders // 'time_in_force' : 'good_til_cancelled', // fill_or_kill, immediate_or_cancel // 'max_show': 123.45, // max amount within an order to be shown to other customers, 0 for invisible order // 'post_only': false, // if the new price would cause the order to be filled immediately (as taker), the price will be changed to be just below the spread. // 'reject_post_only': false, // if true the order is put to order book unmodified or request is rejected // 'reduce_only': false, // if true, the order is intended to only reduce a current position // 'stop_price': false, // stop price, required for stop_limit orders // 'trigger': 'index_price', // mark_price, last_price, required for stop_limit orders // 'advanced': 'usd', // 'implv', advanced option order type, options only }; let priceIsRequired = false; let stopPriceIsRequired = false; if (type === 'limit') { priceIsRequired = true; } else if (type === 'stop_limit') { priceIsRequired = true; stopPriceIsRequired = true; } if (priceIsRequired) { if (price !== undefined) { request['price'] = this.priceToPrecision (symbol, price); } else { throw new ArgumentsRequired (this.id + ' createOrder requires a price argument for a ' + type + ' order'); } } if (stopPriceIsRequired) { const stopPrice = this.safeFloat2 (params, 'stop_price', 'stopPrice'); if (stopPrice === undefined) { throw new ArgumentsRequired (this.id + ' createOrder requires a stop_price or stopPrice param for a ' + type + ' order'); } else { request['stop_price'] = this.priceToPrecision (symbol, stopPrice); } } const method = 'privateGet' + this.capitalize (side); const response = await this[method] (this.extend (request, params)); // // { // "jsonrpc": "2.0", // "id": 5275, // "result": { // "trades": [ // { // "trade_seq": 14151, // "trade_id": "ETH-37435", // "timestamp": 1550657341322, // "tick_direction": 2, // "state": "closed", // "self_trade": false, // "price": 143.81, // "order_type": "market", // "order_id": "ETH-349249", // "matching_id": null, // "liquidity": "T", // "label": "market0000234", // "instrument_name": "ETH-PERPETUAL", // "index_price": 143.73, // "fee_currency": "ETH", // "fee": 0.000139, // "direction": "buy", // "amount": 40 // } // ], // "order": { // "time_in_force": "good_til_cancelled", // "reduce_only": false, // "profit_loss": 0, // "price": "market_price", // "post_only": false, // "order_type": "market", // "order_state": "filled", // "order_id": "ETH-349249", // "max_show": 40, // "last_update_timestamp": 1550657341322, // "label": "market0000234", // "is_liquidation": false, // "instrument_name": "ETH-PERPETUAL", // "filled_amount": 40, // "direction": "buy", // "creation_timestamp": 1550657341322, // "commission": 0.000139, // "average_price": 143.81, // "api": true, // "amount": 40 // } // } // } // const result = this.safeValue (response, 'result', {}); const order = this.safeValue (result, 'order'); const trades = this.safeValue (result, 'trades', []); order['trades'] = trades; return this.parseOrder (order, market); } async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { if (amount === undefined) { throw new ArgumentsRequired (this.id + ' editOrder requires an amount argument'); } if (price === undefined) { throw new ArgumentsRequired (this.id + ' editOrder requires a price argument'); } await this.loadMarkets (); const request = { 'order_id': id, // for perpetual and futures the amount is in USD // for options it is in corresponding cryptocurrency contracts, e.g., BTC or ETH 'amount': this.amountToPrecision (symbol, amount), 'price': this.priceToPrecision (symbol, price), // required // 'post_only': false, // if the new price would cause the order to be filled immediately (as taker), the price will be changed to be just below the spread. // 'reject_post_only': false, // if true the order is put to order book unmodified or request is rejected // 'reduce_only': false, // if true, the order is intended to only reduce a current position // 'stop_price': false, // stop price, required for stop_limit orders // 'advanced': 'usd', // 'implv', advanced option order type, options only }; const response = await this.privateGetEdit (this.extend (request, params)); const result = this.safeValue (response, 'result', {}); const order = this.safeValue (result, 'order'); const trades = this.safeValue (result, 'trades', []); order['trades'] = trades; return this.parseOrder (order); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privateGetCancel (this.extend (request, params)); const result = this.safeValue (response, 'result', {}); return this.parseOrder (result); } async cancelAllOrders (symbol = undefined, params = {}) { await this.loadMarkets (); const request = {}; let method = undefined; if (symbol === undefined) { method = 'privateGetCancelAll'; } else { method = 'privateGetCancelAllByInstrument'; const market = this.market (symbol); request['instrument_name'] = market['id']; } const response = await this[method] (this.extend (request, params)); return response; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; let method = undefined; if (symbol === undefined) { const code = this.codeFromOptions ('fetchOpenOrders'); const currency = this.currency (code); request['currency'] = currency['id']; method = 'privateGetGetOpenOrdersByCurrency'; } else { market = this.market (symbol); request['instrument_name'] = market['id']; method = 'privateGetGetOpenOrdersByInstrument'; } const response = await this[method] (this.extend (request, params)); const result = this.safeValue (response, 'result', []); return this.parseOrders (result, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; let method = undefined; if (symbol === undefined) { const code = this.codeFromOptions ('fetchClosedOrders'); const currency = this.currency (code); request['currency'] = currency['id']; method = 'privateGetGetOrderHistoryByCurrency'; } else { market = this.market (symbol); request['instrument_name'] = market['id']; method = 'privateGetGetOrderHistoryByInstrument'; } const response = await this[method] (this.extend (request, params)); const result = this.safeValue (response, 'result', []); return this.parseOrders (result, market, since, limit); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privateGetGetUserTradesByOrder (this.extend (request, params)); // // { // "jsonrpc": "2.0", // "id": 9367, // "result": { // "trades": [ // { // "trade_seq": 3, // "trade_id": "ETH-34066", // "timestamp": 1550219814585, // "tick_direction": 1, // "state": "open", // "self_trade": false, // "reduce_only": false, // "price": 0.04, // "post_only": false, // "order_type": "limit", // "order_id": "ETH-334607", // "matching_id": null, // "liquidity": "M", // "iv": 56.83, // "instrument_name": "ETH-22FEB19-120-C", // "index_price": 121.37, // "fee_currency": "ETH", // "fee": 0.0011, // "direction": "buy", // "amount": 11 // }, // ], // "has_more": true // } // } // const result = this.safeValue (response, 'result', {}); const trades = this.safeValue (result, 'trades', []); return this.parseTrades (trades, undefined, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'include_old': true, }; let market = undefined; let method = undefined; if (symbol === undefined) { const code = this.codeFromOptions ('fetchMyTrades'); const currency = this.currency (code); request['currency'] = currency['id']; if (since === undefined) { method = 'privateGetGetUserTradesByCurrency'; } else { method = 'privateGetGetUserTradesByCurrencyAndTime'; request['start_timestamp'] = since; } } else { market = this.market (symbol); request['instrument_name'] = market['id']; if (since === undefined) { method = 'privateGetGetUserTradesByInstrument'; } else { method = 'privateGetGetUserTradesByInstrumentAndTime'; request['start_timestamp'] = since; } } if (limit !== undefined) { request['count'] = limit; // default 10 } const response = await this[method] (this.extend (request, params)); // // { // "jsonrpc": "2.0", // "id": 9367, // "result": { // "trades": [ // { // "trade_seq": 3, // "trade_id": "ETH-34066", // "timestamp": 1550219814585, // "tick_direction": 1, // "state": "open", // "self_trade": false, // "reduce_only": false, // "price": 0.04, // "post_only": false, // "order_type": "limit", // "order_id": "ETH-334607", // "matching_id": null, // "liquidity": "M", // "iv": 56.83, // "instrument_name": "ETH-22FEB19-120-C", // "index_price": 121.37, // "fee_currency": "ETH", // "fee": 0.0011, // "direction": "buy", // "amount": 11 // }, // ], // "has_more": true // } // } // const result = this.safeValue (response, 'result', {}); const trades = this.safeValue (result, 'trades', []); return this.parseTrades (trades, market, since, limit); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; if (limit !== undefined) { request['count'] = limit; } const response = await this.privateGetGetDeposits (this.extend (request, params)); // // { // "jsonrpc": "2.0", // "id": 5611, // "result": { // "count": 1, // "data": [ // { // "address": "2N35qDKDY22zmJq9eSyiAerMD4enJ1xx6ax", // "amount": 5, // "currency": "BTC", // "received_timestamp": 1549295017670, // "state": "completed", // "transaction_id": "230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda", // "updated_timestamp": 1549295130159 // } // ] // } // } // const result = this.safeValue (response, 'result', {}); const data = this.safeValue (result, 'data', []); return this.parseTransactions (data, currency, since, limit, params); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; if (limit !== undefined) { request['count'] = limit; } const response = await this.privateGetGetWithdrawals (this.extend (request, params)); // // { // "jsonrpc": "2.0", // "id": 2745, // "result": { // "count": 1, // "data": [ // { // "address": "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBz", // "amount": 0.5, // "confirmed_timestamp": null, // "created_timestamp": 1550571443070, // "currency": "BTC", // "fee": 0.0001, // "id": 1, // "priority": 0.15, // "state": "unconfirmed", // "transaction_id": null, // "updated_timestamp": 1550571443070 // } // ] // } // } // const result = this.safeValue (response, 'result', {}); const data = this.safeValue (result, 'data', []); return this.parseTransactions (data, currency, since, limit, params); } parseTransactionStatus (status) { const statuses = { 'completed': 'ok', 'unconfirmed': 'pending', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchWithdrawals // // { // "address": "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBz", // "amount": 0.5, // "confirmed_timestamp": null, // "created_timestamp": 1550571443070, // "currency": "BTC", // "fee": 0.0001, // "id": 1, // "priority": 0.15, // "state": "unconfirmed", // "transaction_id": null, // "updated_timestamp": 1550571443070 // } // // fetchDeposits // // { // "address": "2N35qDKDY22zmJq9eSyiAerMD4enJ1xx6ax", // "amount": 5, // "currency": "BTC", // "received_timestamp": 1549295017670, // "state": "completed", // "transaction_id": "230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda", // "updated_timestamp": 1549295130159 // } // const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const timestamp = this.safeInteger2 (transaction, 'created_timestamp', 'received_timestamp'); const updated = this.safeInteger (transaction, 'updated_timestamp'); const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); const address = this.safeString (transaction, 'address'); const feeCost = this.safeFloat (transaction, 'fee'); let type = 'deposit'; let fee = undefined; if (feeCost !== undefined) { type = 'withdrawal'; fee = { 'cost': feeCost, 'currency': code, }; } return { 'info': transaction, 'id': this.safeString (transaction, 'id'), 'txid': this.safeString (transaction, 'transaction_id'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'addressTo': address, 'addressFrom': undefined, 'tag': undefined, 'tagTo': undefined, 'tagFrom': undefined, 'type': type, 'amount': this.safeFloat (transaction, 'amount'), 'currency': code, 'status': status, 'updated': updated, 'fee': fee, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'address': address, // must be in the address book 'amount': amount, // 'priority': 'high', // low, mid, high, very_high, extreme_high, insane // 'tfa': '123456', // if enabled }; if (this.twofa !== undefined) { request['tfa'] = this.oath (); } const response = await this.privateGetWithdraw (this.extend (request, params)); return { 'info': response, 'id': this.safeString (response, 'id'), }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/' + 'api/' + this.version + '/' + api + '/' + path; if (api === 'public') { if (Object.keys (params).length) { request += '?' + this.urlencode (params); } } if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const timestamp = this.milliseconds ().toString (); const requestBody = ''; if (Object.keys (params).length) { request += '?' + this.urlencode (params); } const requestData = method + "\n" + request + "\n" + requestBody + "\n"; // eslint-disable-line quotes const auth = timestamp + "\n" + nonce + "\n" + requestData; // eslint-disable-line quotes const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256'); headers = { 'Authorization': 'deri-hmac-sha256 id=' + this.apiKey + ',ts=' + timestamp + ',sig=' + signature + ',' + 'nonce=' + nonce, }; } const url = this.urls['api'] + request; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (!response) { return; // fallback to default error handler } // // { // jsonrpc: '2.0', // error: { // message: 'Invalid params', // data: { reason: 'invalid currency', param: 'currency' }, // code: -32602 // }, // testnet: false, // usIn: 1583763842150374, // usOut: 1583763842150410, // usDiff: 36 // } // const error = this.safeValue (response, 'error'); if (error !== undefined) { const errorCode = this.safeString (error, 'code'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],77:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { AccountSuspended, BadRequest, BadResponse, NetworkError, DDoSProtection, AuthenticationError, PermissionDenied, ArgumentsRequired, ExchangeError, InsufficientFunds, InvalidOrder, InvalidNonce, OrderNotFound } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class digifinex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'digifinex', 'name': 'DigiFinex', 'countries': [ 'SG' ], 'version': 'v3', 'rateLimit': 900, // 300 for posts 'has': { 'cancelOrders': true, 'fetchOrders': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchTickers': true, 'fetchMyTrades': true, 'fetchLedger': true, }, 'timeframes': { '1m': '1', '5m': '5', '15m': '15', '30m': '30', '1h': '60', '4h': '240', '12h': '720', '1d': '1D', '1w': '1W', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/62184319-304e8880-b366-11e9-99fe-8011d6929195.jpg', 'api': 'https://openapi.digifinex.vip', 'www': 'https://www.digifinex.vip', 'doc': [ 'https://docs.digifinex.vip', ], 'fees': 'https://digifinex.zendesk.com/hc/en-us/articles/360000328422-Fee-Structure-on-DigiFinex', 'referral': 'https://www.digifinex.vip/en-ww/from/DhOzBg/3798****5114', }, 'api': { 'v2': { 'get': [ 'ticker', ], }, 'public': { 'get': [ '{market}/symbols', 'kline', 'margin/currencies', 'margin/symbols', 'markets', // undocumented 'order_book', 'ping', 'spot/symbols', 'time', 'trades', ], }, 'private': { 'get': [ '{market}/financelog', '{market}/mytrades', '{market}/order', '{market}/order/current', '{market}/order/history', 'margin/assets', 'margin/financelog', 'margin/mytrades', 'margin/order', 'margin/order/current', 'margin/order/history', 'margin/positions', 'otc/financelog', 'spot/assets', 'spot/financelog', 'spot/mytrades', 'spot/order', 'spot/order/current', 'spot/order/history', ], 'post': [ '{market}/order/cancel', '{market}/order/new', 'margin/order/cancel', 'margin/order/new', 'margin/position/close', 'spot/order/cancel', 'spot/order/new', 'transfer', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.002, 'taker': 0.002, }, }, 'exceptions': { 'exact': { '10001': [ BadRequest, "Wrong request method, please check it's a GET ot POST request" ], '10002': [ AuthenticationError, 'Invalid ApiKey' ], '10003': [ AuthenticationError, "Sign doesn't match" ], '10004': [ BadRequest, 'Illegal request parameters' ], '10005': [ DDoSProtection, 'Request frequency exceeds the limit' ], '10006': [ PermissionDenied, 'Unauthorized to execute this request' ], '10007': [ PermissionDenied, 'IP address Unauthorized' ], '10008': [ InvalidNonce, 'Timestamp for this request is invalid, timestamp must within 1 minute' ], '10009': [ NetworkError, 'Unexist endpoint, please check endpoint URL' ], '10011': [ AccountSuspended, 'ApiKey expired. Please go to client side to re-create an ApiKey' ], '20001': [ PermissionDenied, 'Trade is not open for this trading pair' ], '20002': [ PermissionDenied, 'Trade of this trading pair is suspended' ], '20003': [ InvalidOrder, 'Invalid price or amount' ], '20007': [ InvalidOrder, 'Price precision error' ], '20008': [ InvalidOrder, 'Amount precision error' ], '20009': [ InvalidOrder, 'Amount is less than the minimum requirement' ], '20010': [ InvalidOrder, 'Cash Amount is less than the minimum requirement' ], '20011': [ InsufficientFunds, 'Insufficient balance' ], '20012': [ BadRequest, 'Invalid trade type, valid value: buy/sell)' ], '20013': [ InvalidOrder, 'No order info found' ], '20014': [ BadRequest, 'Invalid date, Valid format: 2018-07-25)' ], '20015': [ BadRequest, 'Date exceeds the limit' ], '20018': [ PermissionDenied, 'Your trading rights have been banned by the system' ], '20019': [ BadRequest, 'Wrong trading pair symbol. Correct format:"usdt_btc". Quote asset is in the front' ], '20020': [ DDoSProtection, "You have violated the API operation trading rules and temporarily forbid trading. At present, we have certain restrictions on the user's transaction rate and withdrawal rate." ], '50000': [ ExchangeError, 'Exception error' ], }, 'broad': { }, }, 'options': { 'defaultType': 'spot', 'types': [ 'spot', 'margin', 'otc' ], }, 'commonCurrencies': { 'BHT': 'Black House Test', }, }); } async fetchMarketsByType (type, params = {}) { const method = 'publicGet' + this.capitalize (type) + 'Symbols'; const response = await this[method] (params); // // { // "symbol_list": [ // { // "order_types":["LIMIT","MARKET"], // "quote_asset":"USDT", // "minimum_value":2, // "amount_precision":4, // "status":"TRADING", // "minimum_amount":0.001, // "symbol":"LTC_USDT", // "margin_rate":0.3, // "zone":"MAIN", // "base_asset":"LTC", // "price_precision":2 // }, // ], // "code":0 // } // const markets = this.safeValue (response, 'symbols_list', []); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'symbol'); const baseId = this.safeString (market, 'base_asset'); const quoteId = this.safeString (market, 'quote_asset'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'amount_precision'), 'price': this.safeInteger (market, 'price_precision'), }; const limits = { 'amount': { 'min': this.safeFloat (market, 'minimum_amount'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeFloat (market, 'minimum_value'), 'max': undefined, }, }; // // The status is documented in the exchange API docs as follows: // TRADING, HALT (delisted), BREAK (trading paused) // https://docs.digifinex.vip/en-ww/v3/#/public/spot/symbols // However, all spot markets actually have status === 'HALT' // despite that they appear to be active on the exchange website. // Apparently, we can't trust this status. // const status = this.safeString (market, 'status'); // const active = (status === 'TRADING'); // const active = undefined; const spot = (type === 'spot'); const margin = (type === 'margin'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'type': type, 'spot': spot, 'margin': margin, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchMarkets (params = {}) { const response = await this.publicGetMarkets (params); // // { // "data": [ // { // "volume_precision":4, // "price_precision":2, // "market":"btc_usdt", // "min_amount":2, // "min_volume":0.0001 // }, // ], // "date":1564507456, // "code":0 // } // const markets = this.safeValue (response, 'data', []); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'market'); const [ baseId, quoteId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'volume_precision'), 'price': this.safeInteger (market, 'price_precision'), }; const limits = { 'amount': { 'min': this.safeFloat (market, 'min_volume'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeFloat (market, 'min_amount'), 'max': undefined, }, }; const active = undefined; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchBalance (params = {}) { const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const type = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); const method = 'privateGet' + this.capitalize (type) + 'Assets'; const response = await this[method] (params); // // { // "code": 0, // "list": [ // { // "currency": "BTC", // "free": 4723846.89208129, // "total": 0 // } // ] // } const balances = this.safeValue (response, 'list', []); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['used'] = this.safeFloat (balance, 'frozen'); account['free'] = this.safeFloat (balance, 'free'); account['total'] = this.safeFloat (balance, 'total'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 10, max 150 } const response = await this.publicGetOrderBook (this.extend (request, params)); // // { // "bids": [ // [9605.77,0.0016], // [9605.46,0.0003], // [9602.04,0.0127], // ], // "asks": [ // [9627.22,0.025803], // [9627.12,0.168543], // [9626.52,0.0011529], // ], // "date":1564509499, // "code":0 // } // const timestamp = this.safeTimestamp (response, 'date'); return this.parseOrderBook (response, timestamp); } async fetchTickers (symbols = undefined, params = {}) { const apiKey = this.safeValue (params, 'apiKey', this.apiKey); if (!apiKey) { throw new ArgumentsRequired (this.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter'); } await this.loadMarkets (); const request = { 'apiKey': apiKey, }; const response = await this.v2GetTicker (this.extend (request, params)); // // { // "ticker":{ // "btc_eth":{ // "last":0.021957, // "base_vol":2249.3521732227, // "change":-0.6, // "vol":102443.5111, // "sell":0.021978, // "low":0.021791, // "buy":0.021946, // "high":0.022266 // } // }, // "date":1564518452, // "code":0 // } // const result = {}; const tickers = this.safeValue (response, 'ticker', {}); const date = this.safeInteger (response, 'date'); const reversedMarketIds = Object.keys (tickers); for (let i = 0; i < reversedMarketIds.length; i++) { const reversedMarketId = reversedMarketIds[i]; const ticker = this.extend ({ 'date': date, }, tickers[reversedMarketId]); const [ quoteId, baseId ] = reversedMarketId.split ('_'); const marketId = baseId + '_' + quoteId; let market = undefined; let symbol = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { const apiKey = this.safeValue (params, 'apiKey', this.apiKey); if (!apiKey) { throw new ArgumentsRequired (this.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter'); } await this.loadMarkets (); const market = this.market (symbol); // reversed base/quote in v2 const marketId = market['quoteId'] + '_' + market['baseId']; const request = { 'symbol': marketId, 'apiKey': apiKey, }; const response = await this.v2GetTicker (this.extend (request, params)); // // { // "ticker":{ // "btc_eth":{ // "last":0.021957, // "base_vol":2249.3521732227, // "change":-0.6, // "vol":102443.5111, // "sell":0.021978, // "low":0.021791, // "buy":0.021946, // "high":0.022266 // } // }, // "date":1564518452, // "code":0 // } // const date = this.safeInteger (response, 'date'); const ticker = this.safeValue (response, 'ticker', {}); let result = this.safeValue (ticker, marketId, {}); result = this.extend ({ 'date': date }, result); return this.parseTicker (result, market); } parseTicker (ticker, market = undefined) { // // fetchTicker, fetchTickers // // { // "last":0.021957, // "base_vol":2249.3521732227, // "change":-0.6, // "vol":102443.5111, // "sell":0.021978, // "low":0.021791, // "buy":0.021946, // "high":0.022266, // "date"1564518452, // injected from fetchTicker/fetchTickers // } // let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeTimestamp (ticker, 'date'); const last = this.safeFloat (ticker, 'last'); const percentage = this.safeFloat (ticker, 'change'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': percentage, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, 'base_vol'), 'info': ticker, }; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "date":1564520003, // "id":1596149203, // "amount":0.7073, // "type":"buy", // "price":0.02193, // } // // fetchMyTrades (private) // // { // "symbol": "BTC_USDT", // "order_id": "6707cbdcda0edfaa7f4ab509e4cbf966", // "id": 28457, // "price": 0.1, // "amount": 0, // "fee": 0.096, // "fee_currency": "USDT", // "timestamp": 1499865549, // "side": "buy", // "is_maker": true // } // const id = this.safeString (trade, 'id'); const orderId = this.safeString (trade, 'order_id'); const timestamp = this.safeTimestamp2 (trade, 'date', 'timestamp'); const side = this.safeString2 (trade, 'type', 'side'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[market]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const takerOrMaker = this.safeValue (trade, 'is_maker'); const feeCost = this.safeFloat (trade, 'fee'); let fee = undefined; if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'fee_currency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'order': orderId, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'takerOrMaker': takerOrMaker, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 100, max 500 } const response = await this.publicGetTrades (this.extend (request, params)); // // { // "data":[ // { // "date":1564520003, // "id":1596149203, // "amount":0.7073, // "type":"buy", // "price":0.02193, // }, // { // "date":1564520002, // "id":1596149165, // "amount":0.3232, // "type":"sell", // "price":0.021927, // }, // ], // "code": 0, // "date": 1564520003, // } // const data = this.safeValue (response, 'data', []); return this.parseTrades (data, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, // timestamp ohlcv[5], // open ohlcv[3], // high ohlcv[4], // low ohlcv[2], // close ohlcv[1], // volume ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'period': this.timeframes[timeframe], // 'start_time': 1564520003, // starting timestamp, 200 candles before end_time by default // 'end_time': 1564520003, // ending timestamp, current timestamp by default }; if (since !== undefined) { const startTime = parseInt (since / 1000); request['start_time'] = startTime; if (limit !== undefined) { const duration = this.parseTimeframe (timeframe); request['end_time'] = this.sum (startTime, limit * duration); } } else if (limit !== undefined) { const endTime = this.seconds (); const duration = this.parseTimeframe (timeframe); request['startTime'] = this.sum (endTime, -limit * duration); } const response = await this.publicGetKline (this.extend (request, params)); // // { // "code":0, // "data":[ // [1556712900,2205.899,0.029967,0.02997,0.029871,0.029927], // [1556713800,1912.9174,0.029992,0.030014,0.029955,0.02996], // [1556714700,1556.4795,0.029974,0.030019,0.029969,0.02999], // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOHLCVs (data, market, timeframe, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const orderType = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); const request = { 'market': orderType, 'symbol': market['id'], 'amount': this.amountToPrecision (symbol, amount), // 'post_only': 0, // 0 by default, if set to 1 the order will be canceled if it can be executed immediately, making sure there will be no market taking }; let suffix = ''; if (type === 'market') { suffix = '_market'; } else { request['price'] = this.priceToPrecision (symbol, price); } request['type'] = side + suffix; const response = await this.privatePostMarketOrderNew (this.extend (request, params)); // // { // "code": 0, // "order_id": "198361cecdc65f9c8c9bb2fa68faec40" // } // const result = this.parseOrder (response, market); return this.extend (result, { 'symbol': symbol, 'side': side, 'type': type, 'amount': amount, 'price': price, }); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const orderType = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); const request = { 'market': orderType, 'order_id': id, }; const response = await this.privatePostMarketOrderCancel (this.extend (request, params)); // // { // "code": 0, // "success": [ // "198361cecdc65f9c8c9bb2fa68faec40", // "3fb0d98e51c18954f10d439a9cf57de0" // ], // "error": [ // "78a7104e3c65cc0c5a212a53e76d0205" // ] // } // const canceledOrders = this.safeValue (response, 'success', []); const numCanceledOrders = canceledOrders.length; if (numCanceledOrders !== 1) { throw new OrderNotFound (this.id + ' cancelOrder ' + id + ' not found'); } return response; } async cancelOrders (ids, symbol = undefined, params = {}) { await this.loadMarkets (); const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const orderType = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); const request = { 'market': orderType, 'order_id': ids.join (','), }; const response = await this.privatePostCancelOrder (this.extend (request, params)); // // { // "code": 0, // "success": [ // "198361cecdc65f9c8c9bb2fa68faec40", // "3fb0d98e51c18954f10d439a9cf57de0" // ], // "error": [ // "78a7104e3c65cc0c5a212a53e76d0205" // ] // } // const canceledOrders = this.safeValue (response, 'success', []); const numCanceledOrders = canceledOrders.length; if (numCanceledOrders < 1) { throw new OrderNotFound (this.id + ' cancelOrders error'); } return response; } parseOrderStatus (status) { const statuses = { '0': 'open', '1': 'open', // partially filled '2': 'closed', '3': 'canceled', '4': 'canceled', // partially filled and canceled }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "code": 0, // "order_id": "198361cecdc65f9c8c9bb2fa68faec40" // } // // fetchOrder, fetchOpenOrders, fetchOrders // // { // "symbol": "BTC_USDT", // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", // "created_date": 1562303547, // "finished_date": 0, // "price": 0.1, // "amount": 1, // "cash_amount": 1, // "executed_amount": 0, // "avg_price": 0, // "status": 1, // "type": "buy", // "kind": "margin" // } // const id = this.safeString (order, 'order_id'); const timestamp = this.safeTimestamp (order, 'created_date'); const lastTradeTimestamp = this.safeTimestamp (order, 'finished_date'); let side = this.safeString (order, 'type'); let type = undefined; if (side !== undefined) { const parts = side.split ('_'); const numParts = parts.length; if (numParts > 1) { side = parts[0]; type = parts[1]; } else { type = 'limit'; } } const status = this.parseOrderStatus (this.safeString (order, 'status')); if (market === undefined) { const exchange = order['symbol'].toUpperCase (); if (exchange in this.markets_by_id) { market = this.markets_by_id[exchange]; } } let symbol = undefined; const marketId = this.safeString (order, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'executed_amount'); const price = this.safeFloat (order, 'price'); const average = this.safeFloat (order, 'avg_price'); let remaining = undefined; let cost = undefined; if (filled !== undefined) { if (average !== undefined) { cost = filled * average; } if (amount !== undefined) { remaining = Math.max (0, amount - filled); } } return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'filled': filled, 'remaining': remaining, 'cost': cost, 'average': average, 'status': status, 'fee': undefined, 'trades': undefined, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const orderType = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); await this.loadMarkets (); let market = undefined; const request = { 'market': orderType, }; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } const response = await this.privateGetMarketOrderCurrent (this.extend (request, params)); // // { // "code": 0, // "data": [ // { // "symbol": "BTC_USDT", // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", // "created_date": 1562303547, // "finished_date": 0, // "price": 0.1, // "amount": 1, // "cash_amount": 1, // "executed_amount": 0, // "avg_price": 0, // "status": 1, // "type": "buy", // "kind": "margin" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOrders (data, market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const orderType = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); await this.loadMarkets (); let market = undefined; const request = { 'market': orderType, }; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['start_time'] = parseInt (since / 1000); // default 3 days from now, max 30 days } if (limit !== undefined) { request['limit'] = limit; // default 10, max 100 } const response = await this.privateGetMarketOrderHistory (this.extend (request, params)); // // { // "code": 0, // "data": [ // { // "symbol": "BTC_USDT", // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", // "created_date": 1562303547, // "finished_date": 0, // "price": 0.1, // "amount": 1, // "cash_amount": 1, // "executed_amount": 0, // "avg_price": 0, // "status": 1, // "type": "buy", // "kind": "margin" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOrders (data, market, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const orderType = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'market': orderType, 'order_id': id, }; const response = await this.privateGetMarketOrder (this.extend (request, params)); // // { // "code": 0, // "data": [ // { // "symbol": "BTC_USDT", // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", // "created_date": 1562303547, // "finished_date": 0, // "price": 0.1, // "amount": 1, // "cash_amount": 1, // "executed_amount": 0, // "avg_price": 0, // "status": 1, // "type": "buy", // "kind": "margin" // } // ] // } // const data = this.safeValue (response, 'data', {}); return this.parseOrder (data, market); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const orderType = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); await this.loadMarkets (); let market = undefined; const request = { 'market': orderType, }; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['start_time'] = parseInt (since / 1000); // default 3 days from now, max 30 days } if (limit !== undefined) { request['limit'] = limit; // default 10, max 100 } const response = await this.privateGetMarketMytrades (this.extend (request, params)); // // { // "code": 0, // "list": [ // { // "symbol": "BTC_USDT", // "order_id": "6707cbdcda0edfaa7f4ab509e4cbf966", // "id": 28457, // "price": 0.1, // "amount": 0, // "fee": 0.096, // "fee_currency": "USDT", // "timestamp": 1499865549, // "side": "buy", // "is_maker": true // } // ] // } // const data = this.safeValue (response, 'list', []); return this.parseTrades (data, market, since, limit); } parseLedgerEntryType (type) { const types = {}; return this.safeString (types, type, type); } parseLedgerEntry (item, currency = undefined) { // // { // "currency_mark": "BTC", // "type": 100234, // "num": 28457, // "balance": 0.1, // "time": 1546272000 // } // const id = this.safeString (item, 'num'); const account = undefined; const type = this.parseLedgerEntryType (this.safeString (item, 'type')); const code = this.safeCurrencyCode (this.safeString (item, 'currency_mark'), currency); const timestamp = this.safeTimestamp (item, 'time'); const before = undefined; const after = this.safeFloat (item, 'balance'); const status = 'ok'; return { 'info': item, 'id': id, 'direction': undefined, 'account': account, 'referenceId': undefined, 'referenceAccount': undefined, 'type': type, 'currency': code, 'amount': undefined, 'before': before, 'after': after, 'status': status, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': undefined, }; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { const defaultType = this.safeString (this.options, 'defaultType', 'spot'); const orderType = this.safeString (params, 'type', defaultType); params = this.omit (params, 'type'); await this.loadMarkets (); const request = { 'market': orderType, }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency_mark'] = currency['id']; } if (since !== undefined) { request['start_time'] = parseInt (since / 1000); } if (limit !== undefined) { request['limit'] = limit; // default 100, max 1000 } const response = await this.privateGetMarketFinancelog (this.extend (request, params)); // // { // "code": 0, // "data": { // "total": 521, // "finance": [ // { // "currency_mark": "BTC", // "type": 100234, // "num": 28457, // "balance": 0.1, // "time": 1546272000 // } // ] // } // } // const data = this.safeValue (response, 'data', {}); const items = this.safeValue (data, 'finance', []); return this.parseLedger (items, currency, since, limit); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const version = (api === 'v2') ? api : this.version; let url = this.urls['api'] + '/' + version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); const urlencoded = this.urlencode (this.keysort (query)); if (api === 'private') { const nonce = this.nonce ().toString (); const auth = urlencoded; // the signature is not time-limited :\ const signature = this.hmac (this.encode (auth), this.encode (this.secret)); if (method === 'GET') { if (urlencoded) { url += '?' + urlencoded; } } else if (method === 'POST') { headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; if (urlencoded) { body = urlencoded; } } headers = { 'ACCESS-KEY': this.apiKey, 'ACCESS-SIGN': signature, 'ACCESS-TIMESTAMP': nonce, }; } else { if (urlencoded) { url += '?' + urlencoded; } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } dateUTC8 (timestampMS) { const timedelta = this.safeValue (this.options, 'timedelta', 8 * 60 * 60 * 1000); // eight hours return this.ymd (timestampMS + timedelta); } handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { if (!response) { return; // fall back to default error handler } const code = this.safeString (response, 'code'); if (code === '0') { return; // no error } const feedback = this.id + ' ' + responseBody; if (code === undefined) { throw new BadResponse (feedback); } const unknownError = [ ExchangeError, feedback ]; const [ ExceptionClass, message ] = this.safeValue (this.exceptions['exact'], code, unknownError); throw new ExceptionClass (message); } }; },{"./base/Exchange":7,"./base/errors":9}],78:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InvalidNonce, BadRequest, InsufficientFunds, PermissionDenied, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class dsx extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'dsx', 'name': 'DSX', 'countries': [ 'UK' ], 'rateLimit': 1500, 'version': 'v3', 'has': { 'CORS': false, 'createMarketOrder': false, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': false, 'fetchOrderBooks': false, 'createDepositAddress': true, 'fetchDepositAddress': true, 'fetchTransactions': true, 'fetchTickers': true, 'fetchMyTrades': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/76909626-cb2bb100-68bc-11ea-99e0-28ba54f04792.jpg', 'api': { 'public': 'https://dsx.uk/mapi', // market data 'private': 'https://dsx.uk/tapi', // trading 'dwapi': 'https://dsx.uk/dwapi', // deposit/withdraw }, 'www': 'https://dsx.uk', 'doc': [ 'https://dsx.uk/developers/publicApi', ], }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'maker': 0.15 / 100, 'taker': 0.25 / 100, }, }, 'timeframes': { '1m': 'm', '1h': 'h', '1d': 'd', }, 'api': { // market data (public) 'public': { 'get': [ 'barsFromMoment/{pair}/{period}/{start}', 'depth/{pair}', 'info', 'lastBars/{pair}/{period}/{amount}', // period is 'm', 'h' or 'd' 'periodBars/{pair}/{period}/{start}/{end}', 'ticker/{pair}', 'trades/{pair}', ], }, // trading (private) 'private': { 'post': [ 'info/account', 'history/transactions', 'history/trades', 'history/orders', 'orders', 'order/cancel', // 'order/cancel/all', 'order/status', 'order/new', 'volume', 'fees', // trading fee schedule ], }, // deposit / withdraw (private) 'dwapi': { 'post': [ 'deposit/cryptoaddress', 'withdraw/crypto', 'withdraw/fiat', 'withdraw/submit', // 'withdraw/cancel', 'transaction/status', // see 'history/transactions' in private tapi above ], }, }, 'exceptions': { 'exact': { 'Sign is invalid': AuthenticationError, // {"success":0,"error":"Sign is invalid"} 'Order was rejected. Incorrect price.': InvalidOrder, // {"success":0,"error":"Order was rejected. Incorrect price."} "Order was rejected. You don't have enough money.": InsufficientFunds, // {"success":0,"error":"Order was rejected. You don't have enough money."} 'This method is blocked for your pair of keys': PermissionDenied, // {"success":0,"error":"This method is blocked for your pair of keys"} }, 'broad': { 'INVALID_PARAMETER': BadRequest, 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} 'invalid api key': AuthenticationError, 'invalid sign': AuthenticationError, 'api key dont have trade permission': AuthenticationError, 'invalid parameter': InvalidOrder, 'invalid order': InvalidOrder, 'Requests too often': DDoSProtection, 'not available': ExchangeNotAvailable, 'data unavailable': ExchangeNotAvailable, 'external service unavailable': ExchangeNotAvailable, 'nonce is invalid': InvalidNonce, // {"success":0,"error":"Parameter: nonce is invalid"} }, }, 'options': { 'fetchTickersMaxLength': 250, }, 'commonCurrencies': { 'DSH': 'DASH', }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetInfo (params); // // { // "server_time": 1522057909, // "pairs": { // "ethusd": { // "decimal_places": 5, // "min_price": 100, // "max_price": 1500, // "min_amount": 0.01, // "hidden": 0, // "fee": 0, // "amount_decimal_places": 4, // "quoted_currency": "USD", // "base_currency": "ETH" // } // } // } // const markets = this.safeValue (response, 'pairs'); const keys = Object.keys (markets); const result = []; for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = markets[id]; const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quoted_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'decimal_places'), 'price': this.safeInteger (market, 'decimal_places'), }; const amountLimits = { 'min': this.safeFloat (market, 'min_amount'), 'max': this.safeFloat (market, 'max_amount'), }; const priceLimits = { 'min': this.safeFloat (market, 'min_price'), 'max': this.safeFloat (market, 'max_price'), }; const costLimits = { 'min': this.safeFloat (market, 'min_total'), }; const limits = { 'amount': amountLimits, 'price': priceLimits, 'cost': costLimits, }; const hidden = this.safeInteger (market, 'hidden'); const active = (hidden === 0); // see parseMarket below // https://github.com/ccxt/ccxt/pull/5786 const otherId = base.toLowerCase () + quote.toLowerCase (); result.push ({ 'id': id, 'otherId': otherId, // https://github.com/ccxt/ccxt/pull/5786 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostInfoAccount (); // // { // "success" : 1, // "return" : { // "funds" : { // "BTC" : { // "total" : 0, // "available" : 0 // }, // "USD" : { // "total" : 0, // "available" : 0 // }, // "USDT" : { // "total" : 0, // "available" : 0 // } // }, // "rights" : { // "info" : 1, // "trade" : 1 // }, // "transactionCount" : 0, // "openOrders" : 0, // "serverTime" : 1537451465 // } // } // const balances = this.safeValue (response, 'return'); const result = { 'info': response }; const funds = this.safeValue (balances, 'funds'); const currencyIds = Object.keys (funds); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const balance = this.safeValue (funds, currencyId, {}); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['total'] = this.safeFloat (balance, 'total'); result[code] = account; } return this.parseBalance (result); } parseTicker (ticker, market = undefined) { // // { high: 0.03492, // low: 0.03245, // avg: 29.46133, // vol: 500.8661, // vol_cur: 17.000797104, // last: 0.03364, // buy: 0.03362, // sell: 0.03381, // updated: 1537521993, // pair: "ethbtc" } // const timestamp = this.safeTimestamp (ticker, 'updated'); let symbol = undefined; const marketId = this.safeString (ticker, 'pair'); market = this.parseMarket (marketId); if (market !== undefined) { symbol = market['symbol']; } // dsx average is inverted, liqui average is not let average = this.safeFloat (ticker, 'avg'); if (average !== undefined) { if (average > 0) { average = 1 / average; } } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': average, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, 'vol_cur'), 'info': ticker, }; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "amount" : 0.0128, // "price" : 6483.99000, // "timestamp" : 1540334614, // "tid" : 35684364, // "type" : "ask" // } // // fetchMyTrades (private) // // { // "number": "36635882", // <-- this is present if the trade has come from the '/order/status' call // "id": "36635882", // <-- this may have been artifically added by the parseTrades method // "pair": "btcusd", // "type": "buy", // "volume": 0.0595, // "rate": 9750, // "orderId": 77149299, // "timestamp": 1519612317, // "commission": 0.00020825, // "commissionCurrency": "btc" // } // const timestamp = this.safeTimestamp (trade, 'timestamp'); let side = this.safeString (trade, 'type'); if (side === 'ask') { side = 'sell'; } else if (side === 'bid') { side = 'buy'; } const price = this.safeFloat2 (trade, 'rate', 'price'); const id = this.safeString2 (trade, 'number', 'id'); const orderId = this.safeString (trade, 'orderId'); const marketId = this.safeString (trade, 'pair'); market = this.parseMarket (marketId); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const amount = this.safeFloat2 (trade, 'amount', 'volume'); const type = 'limit'; // all trades are still limit trades let takerOrMaker = undefined; let fee = undefined; const feeCost = this.safeFloat (trade, 'commission'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } const isYourOrder = this.safeValue (trade, 'is_your_order'); if (isYourOrder !== undefined) { takerOrMaker = 'taker'; if (isYourOrder) { takerOrMaker = 'maker'; } if (fee === undefined) { fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); } } let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } return { 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, 'info': trade, }; } parseTrades (trades, market = undefined, since = undefined, limit = undefined, params = {}) { let result = []; if (Array.isArray (trades)) { for (let i = 0; i < trades.length; i++) { result.push (this.parseTrade (trades[i], market)); } } else { const ids = Object.keys (trades); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const trade = this.parseTrade (trades[id], market); result.push (this.extend (trade, { 'id': id }, params)); } } result = this.sortBy (result, 'timestamp'); const symbol = (market !== undefined) ? market['symbol'] : undefined; return this.filterBySymbolSinceLimit (result, symbol, since, limit); } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; let key = 'quote'; const rate = market[takerOrMaker]; let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); if (side === 'sell') { cost *= price; } else { key = 'base'; } return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': cost, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = 150, max = 2000 } const response = await this.publicGetDepthPair (this.extend (request, params)); const market_id_in_reponse = (market['id'] in response); if (!market_id_in_reponse) { throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); } const orderbook = response[market['id']]; return this.parseOrderBook (orderbook); } async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let ids = undefined; if (symbols === undefined) { ids = this.ids.join ('-'); // max URL length is 2083 symbols, including http schema, hostname, tld, etc... if (ids.length > 2048) { const numIds = this.ids.length; throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); } } else { ids = this.marketIds (symbols); ids = ids.join ('-'); } const request = { 'pair': ids, }; if (limit !== undefined) { request['limit'] = limit; // default = 150, max = 2000 } const response = await this.publicGetDepthPair (this.extend (request, params)); const result = {}; ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; let symbol = id; if (id in this.markets_by_id) { const market = this.markets_by_id[id]; symbol = market['symbol']; } result[symbol] = this.parseOrderBook (response[id]); } return result; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); let ids = this.ids; if (symbols === undefined) { const numIds = ids.length; ids = ids.join ('-'); const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); // max URL length is 2048 symbols, including http schema, hostname, tld, etc... if (ids.length > this.options['fetchTickersMaxLength']) { throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); } } else { ids = this.marketIds (symbols); ids = ids.join ('-'); } const request = { 'pair': ids, }; const tickers = await this.publicGetTickerPair (this.extend (request, params)); // // { // "bchbtc" : { // "high" : 0.02989, // "low" : 0.02736, // "avg" : 33.90585, // "vol" : 0.65982205, // "vol_cur" : 0.0194604180960, // "last" : 0.03000, // "buy" : 0.02980, // "sell" : 0.03001, // "updated" : 1568104614, // "pair" : "bchbtc" // }, // "ethbtc" : { // "high" : 0.01772, // "low" : 0.01742, // "avg" : 56.89082, // "vol" : 229.247115044, // "vol_cur" : 4.02959737298943, // "last" : 0.01769, // "buy" : 0.01768, // "sell" : 0.01776, // "updated" : 1568104614, // "pair" : "ethbtc" // } // } // const result = {}; const keys = Object.keys (tickers); for (let k = 0; k < keys.length; k++) { const id = keys[k]; const ticker = tickers[id]; let symbol = id; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { const tickers = await this.fetchTickers ([ symbol ], params); return tickers[symbol]; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetTradesPair (this.extend (request, params)); if (Array.isArray (response)) { const numElements = response.length; if (numElements === 0) { return []; } } return this.parseTrades (response[market['id']], market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // { // "high" : 0.01955, // "open" : 0.01955, // "low" : 0.01955, // "close" : 0.01955, // "amount" : 2.5, // "timestamp" : 1565155740000 // } // return [ this.safeInteger (ohlcv, 'timestamp'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'amount'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'period': this.timeframes[timeframe], }; let method = 'publicGetLastBarsPairPeriodAmount'; if (since === undefined) { if (limit === undefined) { limit = 100; // required, max 2000 } request['amount'] = limit; } else { method = 'publicGetPeriodBarsPairPeriodStartEnd'; // in their docs they expect milliseconds // but it returns empty arrays with milliseconds // however, it does work properly with seconds request['start'] = parseInt (since / 1000); if (limit === undefined) { request['end'] = this.seconds (); } else { const duration = this.parseTimeframe (timeframe) * 1000; const end = this.sum (since, duration * limit); request['end'] = parseInt (end / 1000); } } const response = await this[method] (this.extend (request, params)); // // { // "ethbtc": [ // { // "high" : 0.01955, // "open" : 0.01955, // "low" : 0.01955, // "close" : 0.01955, // "amount" : 2.5, // "timestamp" : 1565155740000 // }, // { // "high" : 0.01967, // "open" : 0.01967, // "low" : 0.01967, // "close" : 0.01967, // "amount" : 0, // "timestamp" : 1565155680000 // } // ] // } // const candles = this.safeValue (response, market['id'], []); return this.parseOHLCVs (candles, market, timeframe, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (type === 'market' && price === undefined) { throw new ArgumentsRequired (this.id + ' createOrder requires a price argument even for market orders, that is the worst price that you agree to fill your order for'); } const request = { 'pair': market['id'], 'type': side, 'volume': this.amountToPrecision (symbol, amount), 'rate': this.priceToPrecision (symbol, price), 'orderType': type, }; price = parseFloat (price); amount = parseFloat (amount); const response = await this.privatePostOrderNew (this.extend (request, params)); // // { // "success": 1, // "return": { // "received": 0, // "remains": 10, // "funds": { // "BTC": { // "total": 100, // "available": 95 // }, // "USD": { // "total": 10000, // "available": 9995 // }, // "EUR": { // "total": 1000, // "available": 995 // }, // "LTC": { // "total": 1000, // "available": 995 // } // }, // "orderId": 0, // https://github.com/ccxt/ccxt/issues/3677 // } // } // let status = 'open'; let filled = 0.0; let remaining = amount; const responseReturn = this.safeValue (response, 'return'); let id = this.safeString2 (responseReturn, 'orderId', 'order_id'); if (id === '0') { id = this.safeString (responseReturn, 'initOrderId', 'init_order_id'); status = 'closed'; } filled = this.safeFloat (responseReturn, 'received', 0.0); remaining = this.safeFloat (responseReturn, 'remains', amount); const timestamp = this.milliseconds (); return { 'info': response, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': price * filled, 'amount': amount, 'remaining': remaining, 'filled': filled, 'fee': undefined, // 'trades': this.parseTrades (order['trades'], market), 'clientOrderId': undefined, 'average': undefined, 'trades': undefined, }; } parseOrderStatus (status) { const statuses = { '0': 'open', // Active '1': 'closed', // Filled '2': 'canceled', // Killed '3': 'canceling', // Killing '7': 'canceled', // Rejected }; return this.safeString (statuses, status, status); } parseMarket (id) { if (id in this.markets_by_id) { return this.markets_by_id[id]; } else { // the following is a fix for // https://github.com/ccxt/ccxt/pull/5786 // https://github.com/ccxt/ccxt/issues/5770 let markets_by_other_id = this.safeValue (this.options, 'markets_by_other_id'); if (markets_by_other_id === undefined) { this.options['markets_by_other_id'] = this.indexBy (this.markets, 'otherId'); markets_by_other_id = this.options['markets_by_other_id']; } if (id in markets_by_other_id) { return markets_by_other_id[id]; } } return undefined; } parseOrder (order, market = undefined) { // // fetchOrder // // { // "number": 36635882, // "pair": "btcusd", // "type": "buy", // "remainingVolume": 10, // "volume": 10, // "rate": 1000.0, // "timestampCreated": 1496670, // "status": 0, // "orderType": "limit", // "deals": [ // { // "pair": "btcusd", // "type": "buy", // "amount": 1, // "rate": 1000.0, // "orderId": 1, // "timestamp": 1496672724, // "commission": 0.001, // "commissionCurrency": "btc" // } // ] // } // const id = this.safeString (order, 'id'); const status = this.parseOrderStatus (this.safeString (order, 'status')); const timestamp = this.safeTimestamp (order, 'timestampCreated'); const marketId = this.safeString (order, 'pair'); market = this.parseMarket (marketId); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const remaining = this.safeFloat (order, 'remainingVolume'); const amount = this.safeFloat (order, 'volume'); const price = this.safeFloat (order, 'rate'); let filled = undefined; let cost = undefined; if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; cost = price * filled; } } const orderType = this.safeString (order, 'orderType'); const side = this.safeString (order, 'type'); let fee = undefined; const deals = this.safeValue (order, 'deals', []); const numDeals = deals.length; let trades = undefined; let lastTradeTimestamp = undefined; if (numDeals > 0) { trades = this.parseTrades (deals); let feeCost = undefined; let feeCurrency = undefined; for (let i = 0; i < trades.length; i++) { const trade = trades[i]; if (feeCost === undefined) { feeCost = 0; } feeCost = this.sum (feeCost, trade['fee']['cost']); feeCurrency = trade['fee']['currency']; lastTradeTimestamp = trade['timestamp']; } if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': feeCurrency, }; } } return { 'info': order, 'id': id, 'clientOrderId': undefined, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'type': orderType, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'remaining': remaining, 'filled': filled, 'status': status, 'fee': fee, 'trades': trades, 'average': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': parseInt (id), }; const response = await this.privatePostOrderStatus (this.extend (request, params)); // // { // "success": 1, // "return": { // "pair": "btcusd", // "type": "buy", // "remainingVolume": 10, // "volume": 10, // "rate": 1000.0, // "timestampCreated": 1496670, // "status": 0, // "orderType": "limit", // "deals": [ // { // "pair": "btcusd", // "type": "buy", // "amount": 1, // "rate": 1000.0, // "orderId": 1, // "timestamp": 1496672724, // "commission": 0.001, // "commissionCurrency": "btc" // } // ] // } // } // return this.parseOrder (this.extend ({ 'id': id, }, response['return'])); } parseOrdersById (orders, symbol = undefined, since = undefined, limit = undefined) { const ids = Object.keys (orders); const result = []; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const order = this.parseOrder (this.extend ({ 'id': id.toString (), }, orders[id])); result.push (order); } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'count': 10, // Decimal, The maximum number of orders to return // 'fromId': 123, // Decimal, ID of the first order of the selection // 'endId': 321, // Decimal, ID of the last order of the selection // 'order': 'ASC', // String, Order in which orders shown. Possible values are "ASC" — from first to last, "DESC" — from last to first. }; const response = await this.privatePostOrders (this.extend (request, params)); // // { // "success": 1, // "return": { // "0": { // "pair": "btcusd", // "type": "buy", // "remainingVolume": 10, // "volume": 10, // "rate": 1000.0, // "timestampCreated": 1496670, // "status": 0, // "orderType": "limit" // } // } // } // return this.parseOrdersById (this.safeValue (response, 'return', {}), symbol, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'count': 10, // Decimal, The maximum number of orders to return // 'fromId': 123, // Decimal, ID of the first order of the selection // 'endId': 321, // Decimal, ID of the last order of the selection // 'order': 'ASC', // String, Order in which orders shown. Possible values are "ASC" — from first to last, "DESC" — from last to first. }; if (limit !== undefined) { request['count'] = limit; } const response = await this.privatePostHistoryOrders (this.extend (request, params)); // // { // "success": 1, // "return": { // "0": { // "pair": "btcusd", // "type": "buy", // "remainingVolume": 10, // "volume": 10, // "rate": 1000.0, // "timestampCreated": 1496670, // "status": 0, // "orderType": "limit" // } // } // } // return this.parseOrdersById (this.safeValue (response, 'return', {}), symbol, since, limit); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; const response = await this.privatePostOrderCancel (this.extend (request, params)); if (id in this.orders) { this.orders[id]['status'] = 'canceled'; } return response; } parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { const result = []; const ids = Object.keys (orders); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } for (let i = 0; i < ids.length; i++) { const id = ids[i]; const order = this.extend ({ 'id': id }, orders[id]); result.push (this.extend (this.parseOrder (order, market), params)); } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'closed'); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; // some derived classes use camelcase notation for request fields const request = { // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) // 'count': 1000, // the number of trades for display numerical, default = 1000 // 'from_id': trade ID, from which the display starts numerical 0 // 'end_id': trade ID on which the display ends numerical ∞ // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) // 'pair': 'eth_btc', // default = all markets }; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } if (limit !== undefined) { request['count'] = parseInt (limit); } if (since !== undefined) { request['since'] = parseInt (since / 1000); } const response = await this.privatePostHistoryTrades (this.extend (request, params)); let trades = []; if ('return' in response) { trades = response['return']; } return this.parseTrades (trades, market, since, limit); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = {}; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (since !== undefined) { request['since'] = since; } if (limit !== undefined) { request['count'] = limit; } const response = await this.privatePostHistoryTransactions (this.extend (request, params)); // // { // "success": 1, // "return": [ // { // "id": 1, // "timestamp": 11, // "type": "Withdraw", // "amount": 1, // "currency": "btc", // "confirmationsCount": 6, // "address": "address", // "status": 2, // "commission": 0.0001 // } // ] // } // const transactions = this.safeValue (response, 'return', []); return this.parseTransactions (transactions, currency, since, limit); } parseTransactionStatus (status) { const statuses = { '1': 'failed', '2': 'ok', '3': 'pending', '4': 'failed', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // { // "id": 1, // "timestamp": 11, // 11 in their docs ( // "type": "Withdraw", // "amount": 1, // "currency": "btc", // "confirmationsCount": 6, // "address": "address", // "status": 2, // "commission": 0.0001 // } // const timestamp = this.safeTimestamp (transaction, 'timestamp'); let type = this.safeString (transaction, 'type'); if (type !== undefined) { if (type === 'Incoming') { type = 'deposit'; } else if (type === 'Withdraw') { type = 'withdrawal'; } } const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); return { 'id': this.safeString (transaction, 'id'), 'txid': this.safeString (transaction, 'txid'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': this.safeString (transaction, 'address'), 'type': type, 'amount': this.safeFloat (transaction, 'amount'), 'currency': code, 'status': status, 'fee': { 'currency': code, 'cost': this.safeFloat (transaction, 'commission'), 'rate': undefined, }, 'info': transaction, }; } async createDepositAddress (code, params = {}) { const request = { 'new': 1, }; const response = await this.fetchDepositAddress (code, this.extend (request, params)); return response; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.dwapiPostDepositCryptoaddress (this.extend (request, params)); const result = this.safeValue (response, 'return', {}); const address = this.safeString (result, 'address'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': undefined, // not documented in DSX API 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const commission = this.safeValue (params, 'commission'); if (commission === undefined) { throw new ArgumentsRequired (this.id + ' withdraw() requires a `commission` (withdrawal fee) parameter (string)'); } params = this.omit (params, commission); const request = { 'currency': currency['id'], 'amount': parseFloat (amount), 'address': address, 'commission': commission, }; if (tag !== undefined) { request['address'] += ':' + tag; } const response = await this.dwapiPostWithdrawCrypto (this.extend (request, params)); // // [ // { // "success": 1, // "return": { // "transactionId": 2863073 // } // } // ] // const data = this.safeValue (response, 'return', {}); const id = this.safeString (data, 'transactionId'); return { 'info': response, 'id': id, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; const query = this.omit (params, this.extractParams (path)); if (api === 'private' || api === 'dwapi') { url += '/' + this.version + '/' + this.implodeParams (path, params); this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'nonce': nonce, }, query)); const signature = this.decode (this.hmac (this.encode (body), this.encode (this.secret), 'sha512', 'base64')); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': signature, }; } else if (api === 'public') { url += '/' + this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { url += '/' + this.implodeParams (path, params); if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { if (Object.keys (query).length) { body = this.json (query); headers = { 'Content-Type': 'application/json', }; } } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('success' in response) { // // 1 - Liqui only returns the integer 'success' key from their private API // // { "success": 1, ... } httpCode === 200 // { "success": 0, ... } httpCode === 200 // // 2 - However, exchanges derived from Liqui, can return non-integers // // It can be a numeric string // { "sucesss": "1", ... } // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) // // Or just a string // { "success": "true", ... } // { "success": "false", ... }, httpCode >= 200 // // Or a boolean // { "success": true, ... } // { "success": false, ... }, httpCode >= 200 // // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types // // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui // // To cover points 1, 2, 3 and 4 combined this handler should work like this: // let success = this.safeValue (response, 'success', false); if (typeof success === 'string') { if ((success === 'true') || (success === '1')) { success = true; } else { success = false; } } if (!success) { const code = this.safeString (response, 'code'); const message = this.safeString (response, 'error'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } } }; },{"./base/Exchange":7,"./base/errors":9}],79:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ArgumentsRequired, ExchangeError, OrderNotFound, AuthenticationError, InsufficientFunds, InvalidOrder, InvalidNonce, NotSupported, OnMaintenance, RateLimitExceeded, BadRequest, PermissionDenied } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class exmo extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'exmo', 'name': 'EXMO', 'countries': [ 'ES', 'RU' ], // Spain, Russia 'rateLimit': 350, // once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second 'version': 'v1.1', 'has': { 'CORS': false, 'fetchClosedOrders': 'emulated', 'fetchDepositAddress': true, 'fetchOpenOrders': true, 'fetchOrder': 'emulated', 'fetchOrders': 'emulated', 'fetchOrderTrades': true, 'fetchOrderBooks': true, 'fetchMyTrades': true, 'fetchTickers': true, 'withdraw': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'fetchFundingFees': true, 'fetchCurrencies': true, 'fetchTransactions': true, 'fetchOHLCV': true, }, 'timeframes': { '1m': '1', '5m': '5', '15m': '15', '30m': '30', '45m': '45', '1h': '60', '2h': '120', '3h': '180', '4h': '240', '1d': 'D', '1w': 'W', '1M': 'M', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg', 'api': { 'public': 'https://api.exmo.com', 'private': 'https://api.exmo.com', 'web': 'https://exmo.me', }, 'www': 'https://exmo.me', 'referral': 'https://exmo.me/?ref=131685', 'doc': [ 'https://exmo.me/en/api_doc?ref=131685', 'https://github.com/exmo-dev/exmo_api_lib/tree/master/nodejs', ], 'fees': 'https://exmo.com/en/docs/fees', }, 'api': { 'web': { 'get': [ 'ctrl/feesAndLimits', 'en/docs/fees', ], }, 'public': { 'get': [ 'currency', 'order_book', 'pair_settings', 'ticker', 'trades', 'candles_history', ], }, 'private': { 'post': [ 'user_info', 'order_create', 'order_cancel', 'stop_market_order_create', 'stop_market_order_cancel', 'user_open_orders', 'user_trades', 'user_cancelled_orders', 'order_trades', 'required_amount', 'deposit_address', 'withdraw_crypt', 'withdraw_get_txid', 'excode_create', 'excode_load', 'wallet_history', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.2 / 100, 'taker': 0.2 / 100, }, 'funding': { 'tierBased': false, 'percentage': false, // fixed funding fees for crypto, see fetchFundingFees below }, }, 'options': { 'useWebapiForFetchingFees': false, // TODO: figure why Exmo bans us when we try to fetch() their web urls 'feesAndLimits': { 'success': 1, 'ctlr': 'feesAndLimits', 'error': '', 'data': { 'limits': [ { 'pair': 'ALGO/EXM', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.001', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' }, { 'pair': 'ALGO/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '50' }, { 'pair': 'ATOM/EXM', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '50000000' }, { 'pair': 'ATOM/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'BTC/USD', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' }, { 'pair': 'BTC/RUB', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '2000000', 'min_a': '10', 'max_a': '50000000' }, { 'pair': 'BTC/EUR', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' }, { 'pair': 'BTC/UAH', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '15000000', 'min_a': '10', 'max_a': '15000000' }, { 'pair': 'BTC/GBP', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' }, { 'pair': 'ETH/GBP', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'XRP/GBP', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, { 'pair': 'USDT/GBP', 'min_q': '1', 'max_q': '500000', 'min_p': '0.5', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'BTC/PLN', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '20000000', 'min_a': '50', 'max_a': '2000000' }, { 'pair': 'BTC/TRY', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '800000', 'min_a': '40', 'max_a': '6000000' }, { 'pair': 'BTC/KZT', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1000', 'max_p': '12000000', 'min_a': '1000', 'max_a': '100000000' }, { 'pair': 'BTC/USDT', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.01', 'max_p': '30000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'ETH/BTC', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'ETH/USD', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'ETH/RUB', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' }, { 'pair': 'XRP/BTC', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'XRP/USD', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, { 'pair': 'XRP/RUB', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '50000000' }, { 'pair': 'ADA/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'ADA/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' }, { 'pair': 'ADA/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, { 'pair': 'ALGO/USDT', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, { 'pair': 'ALGO/RUB', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' }, { 'pair': 'ALGO/EUR', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, { 'pair': 'ATOM/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, { 'pair': 'ATOM/EUR', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, { 'pair': 'BCH/USD', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '30000', 'min_a': '0.0001', 'max_a': '500000' }, { 'pair': 'BCH/RUB', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '2000000', 'min_a': '0.0001', 'max_a': '50000000' }, { 'pair': 'BCH/EUR', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'BCH/UAH', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.1', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' }, { 'pair': 'BCH/BTC', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '5', 'min_a': '0.0001', 'max_a': '100' }, { 'pair': 'BCH/ETH', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.0000001', 'max_p': '200', 'min_a': '0.0001', 'max_a': '5000' }, { 'pair': 'BCH/USDT', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'BTCZ/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'BTG/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'BTG/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'BTG/ETH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.01', 'max_a': '5000' }, { 'pair': 'BTT/RUB', 'min_q': '1', 'max_q': '500000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' }, { 'pair': 'BTT/UAH', 'min_q': '1', 'max_q': '500000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' }, { 'pair': 'BTT/BTC', 'min_q': '1', 'max_q': '500000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'CRON/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'CRON/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' }, { 'pair': 'CRON/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, { 'pair': 'CRON/EXM', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' }, { 'pair': 'DAI/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'DAI/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '0.5', 'max_a': '30000000' }, { 'pair': 'DAI/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'DAI/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '5000' }, { 'pair': 'DASH/USD', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'DASH/RUB', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' }, { 'pair': 'DASH/UAH', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.01', 'max_p': '200000', 'min_a': '10', 'max_a': '15000000' }, { 'pair': 'DASH/BTC', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'DASH/USDT', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'DCR/RUB', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '3000000' }, { 'pair': 'DCR/UAH', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.25', 'max_a': '1000000' }, { 'pair': 'DCR/BTC', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'DOGE/USD', 'min_q': '100', 'max_q': '500000000', 'min_p': '0.0000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, { 'pair': 'DOGE/BTC', 'min_q': '100', 'max_q': '500000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, { 'pair': 'DXT/USD', 'min_q': '100', 'max_q': '50000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'DXT/BTC', 'min_q': '100', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, { 'pair': 'EOS/USD', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, { 'pair': 'EOS/EUR', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, { 'pair': 'EOS/BTC', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'ETC/USD', 'min_q': '0.2', 'max_q': '100000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '500000' }, { 'pair': 'ETC/RUB', 'min_q': '0.2', 'max_q': '100000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '50000000' }, { 'pair': 'ETC/BTC', 'min_q': '0.2', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '0.5', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'ETH/EUR', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'ETH/UAH', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '1000000', 'min_a': '90', 'max_a': '15000000' }, { 'pair': 'ETH/PLN', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '50', 'max_a': '2000000' }, { 'pair': 'ETH/TRY', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.1', 'max_p': '80000', 'min_a': '10', 'max_a': '6000000' }, { 'pair': 'ETH/KZT', 'min_q': '0.001', 'max_q': '5000', 'min_p': '4', 'max_p': '40000000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'ETH/USDT', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'ETH/LTC', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '100000', 'min_a': '0.05', 'max_a': '100000' }, { 'pair': 'ETZ/BTC', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '10' }, { 'pair': 'ETZ/ETH', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'ETZ/USDT', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '1000' }, { 'pair': 'EXM/USDT', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' }, { 'pair': 'EXM/ETH', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' }, { 'pair': 'GAS/USD', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'GAS/BTC', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'GNT/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'GNT/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, { 'pair': 'GUSD/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'GUSD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, { 'pair': 'GUSD/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' }, { 'pair': 'HP/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'HB/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' }, { 'pair': 'INK/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, { 'pair': 'INK/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'INK/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' }, { 'pair': 'KICK/RUB', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, { 'pair': 'KICK/USDT', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' }, { 'pair': 'KICK/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'KICK/ETH', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' }, { 'pair': 'LSK/USD', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.1', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, { 'pair': 'LSK/RUB', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '50000000' }, { 'pair': 'LSK/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' }, { 'pair': 'LTC/USD', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'LTC/RUB', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' }, { 'pair': 'LTC/EUR', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'LTC/UAH', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '5', 'max_a': '18000000' }, { 'pair': 'LTC/BTC', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'MKR/BTC', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.000001', 'max_a': '100' }, { 'pair': 'MKR/DAI', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.5', 'max_p': '500000', 'min_a': '0.005', 'max_a': '500000' }, { 'pair': 'MNC/USD', 'min_q': '10', 'max_q': '500000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' }, { 'pair': 'MNC/BTC', 'min_q': '10', 'max_q': '500000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' }, { 'pair': 'MNC/ETH', 'min_q': '10', 'max_q': '500000000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '1000' }, { 'pair': 'MNX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, { 'pair': 'MNX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'MNX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, { 'pair': 'NEO/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'NEO/RUB', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1500000', 'min_a': '50', 'max_a': '50000000' }, { 'pair': 'NEO/BTC', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'OMG/USD', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, { 'pair': 'OMG/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'OMG/ETH', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, { 'pair': 'ONG/EXM', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '15000000' }, { 'pair': 'ONG/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' }, { 'pair': 'ONG/RUB', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '250000' }, { 'pair': 'ONG/UAH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '50', 'max_a': '6000000' }, { 'pair': 'ONT/EXM', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '15000000' }, { 'pair': 'ONT/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' }, { 'pair': 'ONT/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '6000000' }, { 'pair': 'ONT/UAH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '250000' }, { 'pair': 'PTI/RUB', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '600000', 'min_a': '10', 'max_a': '600000' }, { 'pair': 'PTI/BTC', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' }, { 'pair': 'PTI/EOS', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.0000001', 'max_p': '5000', 'min_a': '0.01', 'max_a': '20000' }, { 'pair': 'PTI/USDT', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' }, { 'pair': 'QTUM/USD', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '10000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'QTUM/BTC', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, { 'pair': 'QTUM/ETH', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' }, { 'pair': 'ROOBEE/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'SMART/USD', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, { 'pair': 'SMART/RUB', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.0001', 'max_p': '100000', 'min_a': '10', 'max_a': '50000000' }, { 'pair': 'SMART/EUR', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, { 'pair': 'SMART/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'TRX/USD', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, { 'pair': 'TRX/EUR', 'min_q': '0.01', 'max_q': '50000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, { 'pair': 'TRX/RUB', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, { 'pair': 'TRX/UAH', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, { 'pair': 'TRX/BTC', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'USDC/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'USDC/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, { 'pair': 'USDC/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '1000' }, { 'pair': 'USDC/USDT', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, { 'pair': 'USDT/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.5', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'USDT/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, { 'pair': 'USDT/EUR', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'USDT/UAH', 'min_q': '0.01', 'max_q': '500000', 'min_p': '1', 'max_p': '3000', 'min_a': '2', 'max_a': '15000000' }, { 'pair': 'USDT/KZT', 'min_q': '1', 'max_q': '500000', 'min_p': '200', 'max_p': '4000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'VLX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'WAVES/USD', 'min_q': '0.5', 'max_q': '500000', 'min_p': '0.001', 'max_p': '3500', 'min_a': '0.5', 'max_a': '500000' }, { 'pair': 'WAVES/RUB', 'min_q': '0.5', 'max_q': '500000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' }, { 'pair': 'WAVES/BTC', 'min_q': '0.5', 'max_q': '500000', 'min_p': '0.000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, { 'pair': 'WAVES/ETH', 'min_q': '0.5', 'max_q': '500000', 'min_p': '0.00001', 'max_p': '30', 'min_a': '0.0035', 'max_a': '3500' }, { 'pair': 'XEM/USD', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'XEM/EUR', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'XEM/UAH', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' }, { 'pair': 'XEM/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00015', 'max_a': '100' }, { 'pair': 'XLM/USD', 'min_q': '0.01', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, { 'pair': 'XLM/RUB', 'min_q': '0.01', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, { 'pair': 'XLM/TRY', 'min_q': '0.01', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '6000000' }, { 'pair': 'XLM/BTC', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'XMR/USD', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'XMR/RUB', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '600000', 'min_a': '10', 'max_a': '16000000' }, { 'pair': 'XMR/EUR', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'XMR/UAH', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '300000', 'min_a': '5', 'max_a': '16000000' }, { 'pair': 'XMR/BTC', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'XMR/ETH', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' }, { 'pair': 'XRP/EUR', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, { 'pair': 'XRP/TRY', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '6000000' }, { 'pair': 'XRP/UAH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '15000000' }, { 'pair': 'XRP/USDT', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, { 'pair': 'XRP/ETH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' }, { 'pair': 'XTZ/USD', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '100000' }, { 'pair': 'XTZ/RUB', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '500000' }, { 'pair': 'XTZ/BTC', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' }, { 'pair': 'XTZ/ETH', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '1000' }, { 'pair': 'ZEC/USD', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'ZEC/RUB', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, { 'pair': 'ZEC/EUR', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'ZEC/BTC', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.00001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'ZRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, { 'pair': 'ZRX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, { 'pair': 'ZRX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, { 'pair': 'ZAG/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, { 'pair': 'USD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, { 'pair': 'EXM/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0000001', 'max_a': '1' }, { 'pair': 'ATMCASH/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' }, ], 'fees': [ { 'group': 'crypto', 'title': 'Криптовалюта', 'items': [ { 'prov': 'EXM', 'dep': '0%', 'wd': '1 EXM' }, { 'prov': 'BTC', 'dep': '0%', 'wd': '0.0005 BTC' }, { 'prov': 'LTC', 'dep': '0%', 'wd': '0.01 LTC' }, { 'prov': 'DOGE', 'dep': '0%', 'wd': '1 DOGE' }, { 'prov': 'DASH', 'dep': '0%', 'wd': '0.01 DASH' }, { 'prov': 'ETH', 'dep': '0%', 'wd': '0.01 ETH' }, { 'prov': 'WAVES', 'dep': '0%', 'wd': '0.001 WAVES' }, { 'prov': 'ZEC', 'dep': '0%', 'wd': '0.001 ZEC' }, { 'prov': 'USDT', 'dep': '0%', 'wd': '5 USDT' }, { 'prov': 'XMR', 'dep': '0%', 'wd': '0.05 XMR' }, { 'prov': 'XRP', 'dep': '0%', 'wd': '0.02 XRP' }, { 'prov': 'KICK', 'dep': '0%', 'wd': '50 KICK' }, { 'prov': 'ETC', 'dep': '0%', 'wd': '0.01 ETC' }, { 'prov': 'BCH', 'dep': '0%', 'wd': '0.001 BCH' }, { 'prov': 'BTG', 'dep': '0%', 'wd': '0.001 BTG' }, { 'prov': 'EOS', 'dep': '0%', 'wd': '0.05 EOS' }, { 'prov': 'BTCZ', 'dep': '0%', 'wd': '5 BTCZ' }, { 'prov': 'DXT', 'dep': '20 DXT', 'wd': '20 DXT' }, { 'prov': 'XLM', 'dep': '0%', 'wd': '0.01 XLM' }, { 'prov': 'MNX', 'dep': '0%', 'wd': '0.01 MNX' }, { 'prov': 'OMG', 'dep': '0.1 OMG', 'wd': '0.5 OMG' }, { 'prov': 'TRX', 'dep': '0%', 'wd': '1 TRX' }, { 'prov': 'ADA', 'dep': '0%', 'wd': '1 ADA' }, { 'prov': 'INK', 'dep': '10 INK', 'wd': '50 INK' }, { 'prov': 'NEO', 'dep': '0%', 'wd': '0%' }, { 'prov': 'GAS', 'dep': '0%', 'wd': '0%' }, { 'prov': 'ZRX', 'dep': '0%', 'wd': '1 ZRX' }, { 'prov': 'GNT', 'dep': '0%', 'wd': '1 GNT' }, { 'prov': 'GUSD', 'dep': '0%', 'wd': '0.5 GUSD' }, { 'prov': 'LSK', 'dep': '0%', 'wd': '0.1 LSK' }, { 'prov': 'XEM', 'dep': '0%', 'wd': '5 XEM' }, { 'prov': 'SMART', 'dep': '0%', 'wd': '0.5 SMART' }, { 'prov': 'QTUM', 'dep': '0%', 'wd': '0.01 QTUM' }, { 'prov': 'HB', 'dep': '0%', 'wd': '10 HB' }, { 'prov': 'DAI', 'dep': '0%', 'wd': '1 DAI' }, { 'prov': 'MKR', 'dep': '0%', 'wd': '0.005 MKR' }, { 'prov': 'MNC', 'dep': '0%', 'wd': '15 MNC' }, { 'prov': 'PTI', 'dep': '0%', 'wd': '10 PTI' }, { 'prov': 'ATMCASH', 'dep': '0%', 'wd': '5 ATMCASH' }, { 'prov': 'ETZ', 'dep': '0%', 'wd': '1 ETZ' }, { 'prov': 'USDC', 'dep': '0%', 'wd': '0.5 USDC' }, { 'prov': 'ROOBEE', 'dep': '0%', 'wd': '200 ROOBEE' }, { 'prov': 'DCR', 'dep': '0%', 'wd': '0.01 DCR' }, { 'prov': 'ZAG', 'dep': '0%', 'wd': '0%' }, { 'prov': 'BTT', 'dep': '0%', 'wd': '100 BTT' }, { 'prov': 'VLX', 'dep': '0%', 'wd': '1 VLX' }, { 'prov': 'CRON', 'dep': '0%', 'wd': '5 CRON' }, { 'prov': 'ONT', 'dep': '0%', 'wd': '1 ONT' }, { 'prov': 'ONG', 'dep': '0%', 'wd': '5 ONG' }, { 'prov': 'ALGO', 'dep': '0%', 'wd': '0.01 ALGO' }, { 'prov': 'ATOM', 'dep': '0%', 'wd': '' }, ], }, { 'group': 'usd', 'title': 'USD', 'items': [ { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, { 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' }, { 'prov': 'AdvCash', 'dep': '0%', 'wd': '3.49%' }, { 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 0.5 USD', 'wd': '-' }, { 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' }, { 'prov': 'Frick Bank', 'dep': '0 USD', 'wd': '-' }, ], }, { 'group': 'eur', 'title': 'EUR', 'items': [ { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, { 'prov': 'Visa/MasterCard', 'dep': '4.5% + 0.5 EUR', 'wd': '-' }, { 'prov': 'Visa', 'dep': '2.95%', 'wd': '-' }, { 'prov': 'Frick Internal Transfer', 'dep': '0 EUR', 'wd': '-' }, { 'prov': 'SEPA Frick Bank', 'dep': '0 EUR', 'wd': '-' }, { 'prov': 'WIRE Frick Bank', 'dep': '0%', 'wd': '-' }, { 'prov': 'SEPA Weg Ag', 'dep': '-', 'wd': '1 EUR' }, ], }, { 'group': 'rub', 'title': 'RUB', 'items': [ { 'prov': 'Payeer', 'dep': '2.49%', 'wd': '3.99%' }, { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, { 'prov': 'Qiwi', 'dep': '1.49%', 'wd': '2.49%' }, { 'prov': 'Yandex Money', 'dep': '1.49%', 'wd': '-' }, { 'prov': 'AdvCash', 'dep': '0%', 'wd': '3.49%' }, { 'prov': 'Visa/MasterCard', 'dep': '2.99%', 'wd': '3.99% + 60 RUB' }, ], }, { 'group': 'pln', 'title': 'PLN', 'items': [ { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, ], }, { 'group': 'try', 'title': 'TRY', 'items': [ { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, { 'prov': 'Visa', 'dep': '3.05%', 'wd': '-' }, { 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 2 TRY', 'wd': '-' }, { 'prov': 'AdvCash', 'dep': '0%', 'wd': '-' }, ], }, { 'group': 'uah', 'title': 'UAH', 'items': [ { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, { 'prov': 'Terminal', 'dep': '2.6%', 'wd': '-' }, { 'prov': 'Visa/MasterCard', 'dep': '1% + 5 UAH', 'wd': '-' }, { 'prov': 'Visa/MasterCard EasyTransfer', 'dep': '-', 'wd': '2.99%' }, ], }, { 'group': 'kzt', 'title': 'KZT', 'items': [ { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, { 'prov': 'Visa/MasterCard RoyalPay', 'dep': '-', 'wd': '2.99% + 450 KZT' }, { 'prov': 'Visa/MasterCard', 'dep': '3.5%', 'wd': '-' }, { 'prov': 'AdvCash', 'dep': '0%', 'wd': '-' }, ], }, ], }, }, }, 'exceptions': { 'exact': { '40005': AuthenticationError, // Authorization error, incorrect signature '40009': InvalidNonce, // '40015': ExchangeError, // API function do not exist '40016': OnMaintenance, // {"result":false,"error":"Error 40016: Maintenance work in progress"} '40017': AuthenticationError, // Wrong API Key '40032': PermissionDenied, // {"result":false,"error":"Error 40032: Access is denied for this API key"} '40034': RateLimitExceeded, // {"result":false,"error":"Error 40034: Access is denied, rate limit is exceeded"} '50052': InsufficientFunds, '50054': InsufficientFunds, '50304': OrderNotFound, // "Order was not found '123456789'" (fetching order trades for an order that does not have trades yet) '50173': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) '50319': InvalidOrder, // Price by order is less than permissible minimum for this pair '50321': InvalidOrder, // Price by order is more than permissible maximum for this pair }, 'broad': { 'range period is too long': BadRequest, 'invalid syntax': BadRequest, 'API rate limit exceeded': RateLimitExceeded, // {"result":false,"error":"API rate limit exceeded for 99.33.55.224. Retry after 60 sec.","history":[],"begin":1579392000,"end":1579478400} }, }, }); } async fetchTradingFees (params = {}) { if (this.options['useWebapiForFetchingFees']) { const response = await this.webGetEnDocsFees (params); let parts = response.split (''); let numParts = parts.length; if (numParts !== 2) { throw new NotSupported (this.id + ' fetchTradingFees format has changed'); } const rest = parts[1]; parts = rest.split (''); numParts = parts.length; if (numParts < 2) { throw new NotSupported (this.id + ' fetchTradingFees format has changed'); } const fee = parseFloat (parts[0].replace ('%', '')) * 0.01; const taker = fee; const maker = fee; return { // 'info': response, 'maker': maker, 'taker': taker, }; } else { return { 'maker': this.fees['trading']['maker'], 'taker': this.fees['trading']['taker'], }; } } parseFixedFloatValue (input) { if ((input === undefined) || (input === '-')) { return undefined; } const isPercentage = (input.indexOf ('%') >= 0); const parts = input.split (' '); const value = parts[0].replace ('%', ''); const result = parseFloat (value); if ((result > 0) && isPercentage) { throw new ExchangeError (this.id + ' parseFixedFloatValue detected an unsupported non-zero percentage-based fee ' + input); } return result; } async fetchFundingFees (params = {}) { let response = undefined; if (this.options['useWebapiForFetchingFees']) { response = await this.webGetCtrlFeesAndLimits (params); } else { response = this.options['feesAndLimits']; } // the code below assumes all non-zero crypto fees are fixed (for now) const withdraw = {}; const deposit = {}; const groups = this.safeValue (response['data'], 'fees'); const groupsByGroup = this.indexBy (groups, 'group'); const items = groupsByGroup['crypto']['items']; for (let i = 0; i < items.length; i++) { const item = items[i]; const code = this.safeCurrencyCode (this.safeString (item, 'prov')); const withdrawalFee = this.safeString (item, 'wd'); const depositFee = this.safeString (item, 'dep'); if (withdrawalFee !== undefined) { if (withdrawalFee.length > 0) { withdraw[code] = this.parseFixedFloatValue (withdrawalFee); } } if (depositFee !== undefined) { if (depositFee.length > 0) { deposit[code] = this.parseFixedFloatValue (depositFee); } } } // sets fiat fees to undefined const fiatGroups = this.toArray (this.omit (groupsByGroup, 'crypto')); for (let i = 0; i < fiatGroups.length; i++) { const code = this.safeCurrencyCode (this.safeString (fiatGroups[i], 'title')); withdraw[code] = undefined; deposit[code] = undefined; } const result = { 'info': response, 'withdraw': withdraw, 'deposit': deposit, }; // cache them for later use this.options['fundingFees'] = result; return result; } async fetchCurrencies (params = {}) { const fees = await this.fetchFundingFees (params); // todo redesign the 'fee' property in currencies const ids = Object.keys (fees['withdraw']); const limitsByMarketId = this.indexBy (fees['info']['data']['limits'], 'pair'); const marketIds = Object.keys (limitsByMarketId); const minAmounts = {}; const minPrices = {}; const minCosts = {}; const maxAmounts = {}; const maxPrices = {}; const maxCosts = {}; for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const limit = limitsByMarketId[marketId]; const [ baseId, quoteId ] = marketId.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const maxAmount = this.safeFloat (limit, 'max_q'); const maxPrice = this.safeFloat (limit, 'max_p'); const maxCost = this.safeFloat (limit, 'max_a'); const minAmount = this.safeFloat (limit, 'min_q'); const minPrice = this.safeFloat (limit, 'min_p'); const minCost = this.safeFloat (limit, 'min_a'); minAmounts[base] = Math.min (this.safeFloat (minAmounts, base, minAmount), minAmount); maxAmounts[base] = Math.max (this.safeFloat (maxAmounts, base, maxAmount), maxAmount); minPrices[quote] = Math.min (this.safeFloat (minPrices, quote, minPrice), minPrice); minCosts[quote] = Math.min (this.safeFloat (minCosts, quote, minCost), minCost); maxPrices[quote] = Math.max (this.safeFloat (maxPrices, quote, maxPrice), maxPrice); maxCosts[quote] = Math.max (this.safeFloat (maxCosts, quote, maxCost), maxCost); } const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const code = this.safeCurrencyCode (id); const fee = this.safeValue (fees['withdraw'], code); const active = true; result[code] = { 'id': id, 'code': code, 'name': code, 'active': active, 'fee': fee, 'precision': 8, 'limits': { 'amount': { 'min': this.safeFloat (minAmounts, code), 'max': this.safeFloat (maxAmounts, code), }, 'price': { 'min': this.safeFloat (minPrices, code), 'max': this.safeFloat (maxPrices, code), }, 'cost': { 'min': this.safeFloat (minCosts, code), 'max': this.safeFloat (maxCosts, code), }, }, 'info': id, }; } return result; } async fetchMarkets (params = {}) { const response = await this.publicGetPairSettings (params); // // { // "EXM_ETH": { // "min_quantity": "1", // "max_quantity": "1000", // "min_price": "1", // "max_price": "1000", // "max_amount": "1000", // "min_amount": "1", // "commission_taker_percent": "0.2", // "commission_maker_percent": "0.2" // }, // } // const keys = Object.keys (response); const result = []; for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = response[id]; const symbol = id.replace ('_', '/'); const [ baseId, quoteId ] = symbol.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const taker = this.safeFloat (market, 'commission_taker_percent'); const maker = this.safeFloat (market, 'commission_maker_percent'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'taker': taker / 100, 'maker': maker / 100, 'limits': { 'amount': { 'min': this.safeFloat (market, 'min_quantity'), 'max': this.safeFloat (market, 'max_quantity'), }, 'price': { 'min': this.safeFloat (market, 'min_price'), 'max': this.safeFloat (market, 'max_price'), }, 'cost': { 'min': this.safeFloat (market, 'min_amount'), 'max': this.safeFloat (market, 'max_amount'), }, }, 'precision': { 'amount': 8, 'price': 8, }, 'info': market, }); } return result; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'resolution': this.timeframes[timeframe], }; const options = this.safeValue (this.options, 'fetchOHLCV'); const maxLimit = this.safeInteger (options, 'maxLimit', 3000); const duration = this.parseTimeframe (timeframe); const now = this.milliseconds (); if (since === undefined) { if (limit === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument or a limit argument'); } else { if (limit > maxLimit) { throw new BadRequest (this.id + ' fetchOHLCV will serve ' + maxLimit.toString () + ' candles at most'); } request['from'] = parseInt (now / 1000) - limit * duration - 1; request['to'] = parseInt (now / 1000); } } else { request['from'] = parseInt (since / 1000) - 1; if (limit === undefined) { request['to'] = parseInt (now / 1000); } else { if (limit > maxLimit) { throw new BadRequest (this.id + ' fetchOHLCV will serve ' + maxLimit.toString () + ' candles at most'); } const to = this.sum (since, limit * duration * 1000); request['to'] = parseInt (to / 1000); } } const response = await this.publicGetCandlesHistory (this.extend (request, params)); // // { // "candles":[ // {"t":1584057600000,"o":0.02235144,"c":0.02400233,"h":0.025171,"l":0.02221,"v":5988.34031761}, // {"t":1584144000000,"o":0.0240373,"c":0.02367413,"h":0.024399,"l":0.0235,"v":2027.82522329}, // {"t":1584230400000,"o":0.02363458,"c":0.02319242,"h":0.0237948,"l":0.02223196,"v":1707.96944997}, // ] // } // const candles = this.safeValue (response, 'candles', []); return this.parseOHLCVs (candles, market, timeframe, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { // // { // "t":1584057600000, // "o":0.02235144, // "c":0.02400233, // "h":0.025171, // "l":0.02221, // "v":5988.34031761 // } // return [ this.safeInteger (ohlcv, 't'), this.safeFloat (ohlcv, 'o'), this.safeFloat (ohlcv, 'h'), this.safeFloat (ohlcv, 'l'), this.safeFloat (ohlcv, 'c'), this.safeFloat (ohlcv, 'v'), ]; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostUserInfo (params); const result = { 'info': response }; const codes = Object.keys (this.currencies); for (let i = 0; i < codes.length; i++) { const code = codes[i]; const currencyId = this.currencyId (code); const account = this.account (); if (currencyId in response['balances']) { account['free'] = this.safeFloat (response['balances'], currencyId); } if (currencyId in response['reserved']) { account['used'] = this.safeFloat (response['reserved'], currencyId); } result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrderBook (this.extend (request, params)); const result = this.safeValue (response, market['id']); return this.parseOrderBook (result, undefined, 'bid', 'ask'); } async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let ids = undefined; if (symbols === undefined) { ids = this.ids.join (','); // max URL length is 2083 symbols, including http schema, hostname, tld, etc... if (ids.length > 2048) { const numIds = this.ids.length; throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); } } else { ids = this.marketIds (symbols); ids = ids.join (','); } const request = { 'pair': ids, }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrderBook (this.extend (request, params)); const result = {}; const marketIds = Object.keys (response); for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; let symbol = marketId; if (marketId in this.markets_by_id) { const market = this.markets_by_id[marketId]; symbol = market['symbol']; } result[symbol] = this.parseOrderBook (response[marketId], undefined, 'bid', 'ask'); } return result; } parseTicker (ticker, market = undefined) { const timestamp = this.safeTimestamp (ticker, 'updated'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last_trade'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy_price'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell_price'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker, 'avg'), 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, 'vol_curr'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker (params); const result = {}; const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = this.markets_by_id[id]; const symbol = market['symbol']; const ticker = response[id]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker (params); const market = this.market (symbol); return this.parseTicker (response[market['id']], market); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "trade_id":165087520, // "date":1587470005, // "type":"buy", // "quantity":"1.004", // "price":"0.02491461", // "amount":"0.02501426" // }, // // fetchMyTrades, fetchOrderTrades // // { // "trade_id": 3, // "date": 1435488248, // "type": "buy", // "pair": "BTC_USD", // "order_id": 12345, // "quantity": 1, // "price": 100, // "amount": 100, // "exec_type": "taker", // "commission_amount": "0.02", // "commission_currency": "BTC", // "commission_percent": "0.2" // } // const timestamp = this.safeTimestamp (trade, 'date'); let symbol = undefined; const id = this.safeString (trade, 'trade_id'); const orderId = this.safeString (trade, 'order_id'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'quantity'); const cost = this.safeFloat (trade, 'amount'); const side = this.safeString (trade, 'type'); const type = undefined; const marketId = this.safeString (trade, 'pair'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const takerOrMaker = this.safeString (trade, 'exec_type'); let fee = undefined; const feeCost = this.safeFloat (trade, 'commission_amount'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'commission_currency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); let feeRate = this.safeFloat (trade, 'commission_percent'); if (feeRate !== undefined) { feeRate /= 1000; } fee = { 'cost': feeCost, 'currency': feeCurrencyCode, 'rate': feeRate, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': type, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); // // { // "ETH_BTC":[ // { // "trade_id":165087520, // "date":1587470005, // "type":"buy", // "quantity":"1.004", // "price":"0.02491461", // "amount":"0.02501426" // }, // { // "trade_id":165087369, // "date":1587469938, // "type":"buy", // "quantity":"0.94", // "price":"0.02492348", // "amount":"0.02342807" // } // ] // } // const data = this.safeValue (response, market['id'], []); return this.parseTrades (data, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { // a symbol is required but it can be a single string, or a non-empty array if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument (a single symbol or an array)'); } await this.loadMarkets (); let pair = undefined; let market = undefined; if (Array.isArray (symbol)) { const numSymbols = symbol.length; if (numSymbols < 1) { throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a non-empty symbol array'); } const marketIds = this.marketIds (symbol); pair = marketIds.join (','); } else { market = this.market (symbol); pair = market['id']; } const request = { 'pair': pair, }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privatePostUserTrades (this.extend (request, params)); let result = []; const marketIds = Object.keys (response); for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; let symbol = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } const items = response[marketId]; const trades = this.parseTrades (items, market, since, limit, { 'symbol': symbol, }); result = this.arrayConcat (result, trades); } return this.filterBySinceLimit (result, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const prefix = (type === 'market') ? (type + '_') : ''; const market = this.market (symbol); if ((type === 'market') && (price === undefined)) { price = 0; } const request = { 'pair': market['id'], 'quantity': this.amountToPrecision (symbol, amount), 'type': prefix + side, 'price': this.priceToPrecision (symbol, price), }; const response = await this.privatePostOrderCreate (this.extend (request, params)); const id = this.safeString (response, 'order_id'); const timestamp = this.milliseconds (); amount = parseFloat (amount); price = parseFloat (price); const status = 'open'; const order = { 'id': id, 'info': response, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': price * amount, 'amount': amount, 'remaining': amount, 'filled': 0.0, 'fee': undefined, 'trades': undefined, 'clientOrderId': undefined, 'average': undefined, }; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id }; const response = await this.privatePostOrderCancel (this.extend (request, params)); if (id in this.orders) { this.orders[id]['status'] = 'canceled'; } return response; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); try { const request = { 'order_id': id.toString (), }; const response = await this.privatePostOrderTrades (this.extend (request, params)); // // { // "type": "buy", // "in_currency": "BTC", // "in_amount": "1", // "out_currency": "USD", // "out_amount": "100", // "trades": [ // { // "trade_id": 3, // "date": 1435488248, // "type": "buy", // "pair": "BTC_USD", // "order_id": 12345, // "quantity": 1, // "price": 100, // "amount": 100 // } // ] // } // const order = this.parseOrder (response); return this.extend (order, { 'id': id.toString (), }); } catch (e) { if (e instanceof OrderNotFound) { if (id in this.orders) { return this.orders[id]; } } } throw new OrderNotFound (this.id + ' fetchOrder order id ' + id.toString () + ' not found in cache.'); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'order_id': id.toString (), }; const response = await this.privatePostOrderTrades (this.extend (request, params)); // // { // "type": "buy", // "in_currency": "BTC", // "in_amount": "1", // "out_currency": "USD", // "out_amount": "100", // "trades": [ // { // "trade_id": 3, // "date": 1435488248, // "type": "buy", // "pair": "BTC_USD", // "order_id": 12345, // "quantity": 1, // "price": 100, // "amount": 100, // "exec_type": "taker", // "commission_amount": "0.02", // "commission_currency": "BTC", // "commission_percent": "0.2" // } // ] // } // const trades = this.safeValue (response, 'trades'); return this.parseTrades (trades, market, since, limit); } updateCachedOrders (openOrders, symbol) { // update local cache with open orders for (let j = 0; j < openOrders.length; j++) { const id = openOrders[j]['id']; this.orders[id] = openOrders[j]; } const openOrdersIndexedById = this.indexBy (openOrders, 'id'); const cachedOrderIds = Object.keys (this.orders); for (let k = 0; k < cachedOrderIds.length; k++) { // match each cached order to an order in the open orders array // possible reasons why a cached order may be missing in the open orders array: // - order was closed or canceled -> update cache // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip const id = cachedOrderIds[k]; let order = this.orders[id]; if (!(id in openOrdersIndexedById)) { // cached order is not in open orders array // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order if (symbol !== undefined && symbol !== order['symbol']) { continue; } // order is cached but not present in the list of open orders -> mark the cached order as closed if (order['status'] === 'open') { order = this.extend (order, { 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") 'cost': undefined, 'filled': order['amount'], 'remaining': 0.0, }); if (order['cost'] === undefined) { if (order['filled'] !== undefined) { order['cost'] = order['filled'] * order['price']; } } this.orders[id] = order; } } } return this.toArray (this.orders); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.privatePostUserOpenOrders (params); const marketIds = Object.keys (response); let orders = []; for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; let market = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } const parsedOrders = this.parseOrders (response[marketId], market); orders = this.arrayConcat (orders, parsedOrders); } this.updateCachedOrders (orders, symbol); return this.filterBySymbolSinceLimit (this.toArray (this.orders), symbol, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.fetchOrders (symbol, since, limit, params); const orders = this.filterBy (this.orders, 'status', 'open'); return this.filterBySymbolSinceLimit (orders, symbol, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.fetchOrders (symbol, since, limit, params); const orders = this.filterBy (this.orders, 'status', 'closed'); return this.filterBySymbolSinceLimit (orders, symbol, since, limit); } parseOrder (order, market = undefined) { // // fetchOrders, fetchOpenOrders, fetchClosedOrders // // { // "order_id": "14", // "created": "1435517311", // "type": "buy", // "pair": "BTC_USD", // "price": "100", // "quantity": "1", // "amount": "100" // } // // fetchOrder // // { // "type": "buy", // "in_currency": "BTC", // "in_amount": "1", // "out_currency": "USD", // "out_amount": "100", // "trades": [ // { // "trade_id": 3, // "date": 1435488248, // "type": "buy", // "pair": "BTC_USD", // "order_id": 12345, // "quantity": 1, // "price": 100, // "amount": 100 // } // ] // } // let id = this.safeString (order, 'order_id'); let timestamp = this.safeTimestamp (order, 'created'); let symbol = undefined; const side = this.safeString (order, 'type'); if (market === undefined) { let marketId = undefined; if ('pair' in order) { marketId = order['pair']; } else if (('in_currency' in order) && ('out_currency' in order)) { if (side === 'buy') { marketId = order['in_currency'] + '_' + order['out_currency']; } else { marketId = order['out_currency'] + '_' + order['in_currency']; } } if ((marketId !== undefined) && (marketId in this.markets_by_id)) { market = this.markets_by_id[marketId]; } } let amount = this.safeFloat (order, 'quantity'); if (amount === undefined) { const amountField = (side === 'buy') ? 'in_amount' : 'out_amount'; amount = this.safeFloat (order, amountField); } let price = this.safeFloat (order, 'price'); let cost = this.safeFloat (order, 'amount'); let filled = 0.0; const trades = []; const transactions = this.safeValue (order, 'trades', []); let feeCost = undefined; let lastTradeTimestamp = undefined; let average = undefined; const numTransactions = transactions.length; if (numTransactions > 0) { feeCost = 0; for (let i = 0; i < numTransactions; i++) { const trade = this.parseTrade (transactions[i], market); if (id === undefined) { id = trade['order']; } if (timestamp === undefined) { timestamp = trade['timestamp']; } if (timestamp > trade['timestamp']) { timestamp = trade['timestamp']; } filled = this.sum (filled, trade['amount']); feeCost = this.sum (feeCost, trade['fee']['cost']); trades.push (trade); } lastTradeTimestamp = trades[numTransactions - 1]['timestamp']; } let remaining = undefined; if (amount !== undefined) { remaining = amount - filled; } let status = this.safeString (order, 'status'); // in case we need to redefine it for canceled orders if (filled >= amount) { status = 'closed'; } else { status = 'open'; } if (market === undefined) { market = this.getMarketFromTrades (trades); } let feeCurrency = undefined; if (market !== undefined) { symbol = market['symbol']; feeCurrency = market['quote']; } if (cost === undefined) { if (price !== undefined) { cost = price * filled; } } else { if (filled > 0) { if (average === undefined) { average = cost / filled; } if (price === undefined) { price = cost / filled; } } } const fee = { 'cost': feeCost, 'currency': feeCurrency, }; return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': lastTradeTimestamp, 'status': status, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'average': average, 'trades': trades, 'fee': fee, 'info': order, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const response = await this.privatePostDepositAddress (params); const depositAddress = this.safeString (response, code); let address = undefined; let tag = undefined; if (depositAddress) { const addressAndTag = depositAddress.split (','); address = addressAndTag[0]; const numParts = addressAndTag.length; if (numParts > 1) { tag = addressAndTag[1]; } } this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } getMarketFromTrades (trades) { const tradesBySymbol = this.indexBy (trades, 'pair'); const symbols = Object.keys (tradesBySymbol); const numSymbols = symbols.length; if (numSymbols === 1) { return this.markets[symbols[0]]; } return undefined; } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; const rate = market[takerOrMaker]; let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); let key = 'quote'; if (side === 'sell') { cost *= price; } else { key = 'base'; } return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': parseFloat (this.feeToPrecision (symbol, cost)), }; } async withdraw (code, amount, address, tag = undefined, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'amount': amount, 'currency': currency['id'], 'address': address, }; if (tag !== undefined) { request['invoice'] = tag; } const response = await this.privatePostWithdrawCrypt (this.extend (request, params)); return { 'info': response, 'id': response['task_id'], }; } parseTransactionStatus (status) { const statuses = { 'transferred': 'ok', 'paid': 'ok', 'pending': 'pending', 'processing': 'pending', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchTransactions // // { // "dt": 1461841192, // "type": "deposit", // "curr": "RUB", // "status": "processing", // "provider": "Qiwi (LA) [12345]", // "amount": "1", // "account": "", // "txid": "ec46f784ad976fd7f7539089d1a129fe46...", // } // const timestamp = this.safeTimestamp (transaction, 'dt'); let amount = this.safeFloat (transaction, 'amount'); if (amount !== undefined) { amount = Math.abs (amount); } const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const txid = this.safeString (transaction, 'txid'); const type = this.safeString (transaction, 'type'); const currencyId = this.safeString (transaction, 'curr'); const code = this.safeCurrencyCode (currencyId, currency); let address = this.safeString (transaction, 'account'); if (address !== undefined) { const parts = address.split (':'); const numParts = parts.length; if (numParts === 2) { address = parts[1].replace (' ', ''); } } let fee = undefined; // fixed funding fees only (for now) if (!this.fees['funding']['percentage']) { const key = (type === 'withdrawal') ? 'withdraw' : 'deposit'; let feeCost = this.safeFloat (this.options['fundingFees'][key], code); // users don't pay for cashbacks, no fees for that const provider = this.safeString (transaction, 'provider'); if (provider === 'cashback') { feeCost = 0; } if (feeCost !== undefined) { // withdrawal amount includes the fee if (type === 'withdrawal') { amount = amount - feeCost; } fee = { 'cost': feeCost, 'currency': code, 'rate': undefined, }; } } return { 'info': transaction, 'id': undefined, 'currency': code, 'amount': amount, 'address': address, 'tag': undefined, // refix it properly 'status': status, 'type': type, 'updated': undefined, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': fee, }; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (since !== undefined) { request['date'] = parseInt (since / 1000); } let currency = undefined; if (code !== undefined) { currency = this.currency (code); } const response = await this.privatePostWalletHistory (this.extend (request, params)); // // { // "result": true, // "error": "", // "begin": "1493942400", // "end": "1494028800", // "history": [ // { // "dt": 1461841192, // "type": "deposit", // "curr": "RUB", // "status": "processing", // "provider": "Qiwi (LA) [12345]", // "amount": "1", // "account": "", // "txid": "ec46f784ad976fd7f7539089d1a129fe46...", // }, // { // "dt": 1463414785, // "type": "withdrawal", // "curr": "USD", // "status": "paid", // "provider": "EXCODE", // "amount": "-1", // "account": "EX-CODE_19371_USDda...", // "txid": "", // }, // ], // } // return this.parseTransactions (response['history'], currency, since, limit); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/'; if (api !== 'web') { url += this.version + '/'; } url += path; if ((api === 'public') || (api === 'web')) { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'nonce': nonce }, params)); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } nonce () { return this.milliseconds (); } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if (('result' in response) || ('errmsg' in response)) { // // {"result":false,"error":"Error 50052: Insufficient funds"} // {"s":"error","errmsg":"strconv.ParseInt: parsing \"\": invalid syntax"} // let success = this.safeValue (response, 'result', false); if (typeof success === 'string') { if ((success === 'true') || (success === '1')) { success = true; } else { success = false; } } if (!success) { let code = undefined; const message = this.safeString2 (response, 'error', 'errmsg'); const errorParts = message.split (':'); const numParts = errorParts.length; if (numParts > 1) { const errorSubParts = errorParts[0].split (' '); const numSubParts = errorSubParts.length; code = (numSubParts > 1) ? errorSubParts[1] : errorSubParts[0]; } const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9}],80:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, ExchangeNotAvailable } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class exx extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'exx', 'name': 'EXX', 'countries': [ 'CN' ], 'rateLimit': 1000 / 10, 'userAgent': this.userAgents['chrome'], 'has': { 'fetchOrder': true, 'fetchTickers': true, 'fetchOpenOrders': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/37770292-fbf613d0-2de4-11e8-9f79-f2dc451b8ccb.jpg', 'api': { 'public': 'https://api.exx.com/data/v1', 'private': 'https://trade.exx.com/api', }, 'www': 'https://www.exx.com/', 'doc': 'https://www.exx.com/help/restApi', 'fees': 'https://www.exx.com/help/rate', 'referral': 'https://www.exx.com/r/fde4260159e53ab8a58cc9186d35501f?recommQd=1', }, 'api': { 'public': { 'get': [ 'markets', 'tickers', 'ticker', 'depth', 'trades', ], }, 'private': { 'get': [ 'order', 'cancel', 'getOrder', 'getOpenOrders', 'getBalance', ], }, }, 'fees': { 'trading': { 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, 'funding': { 'withdraw': { 'BCC': 0.0003, 'BCD': 0.0, 'BOT': 10.0, 'BTC': 0.001, 'BTG': 0.0, 'BTM': 25.0, 'BTS': 3.0, 'EOS': 1.0, 'ETC': 0.01, 'ETH': 0.01, 'ETP': 0.012, 'HPY': 0.0, 'HSR': 0.001, 'INK': 20.0, 'LTC': 0.005, 'MCO': 0.6, 'MONA': 0.01, 'QASH': 5.0, 'QCASH': 5.0, 'QTUM': 0.01, 'USDT': 5.0, }, }, }, 'commonCurrencies': { 'TV': 'TIV', // Ti-Value }, 'exceptions': { '103': AuthenticationError, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetMarkets (params); const ids = Object.keys (response); const result = []; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = response[id]; const [ baseId, quoteId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const active = market['isOpen'] === true; const precision = { 'amount': parseInt (market['amountScale']), 'price': parseInt (market['priceScale']), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }); } return result; } parseTicker (ticker, market = undefined) { const symbol = market['symbol']; const timestamp = this.safeInteger (ticker, 'date'); ticker = ticker['ticker']; const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeFloat (ticker, 'riseRate'), 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], }; const response = await this.publicGetTicker (this.extend (request, params)); return this.parseTicker (response, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickers (params); const result = {}; const timestamp = this.milliseconds (); const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; if (!(id in this.marketsById)) { continue; } const market = this.marketsById[id]; const symbol = market['symbol']; const ticker = { 'date': timestamp, 'ticker': response[id], }; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'currency': this.marketId (symbol), }; const response = await this.publicGetDepth (this.extend (request, params)); const timestamp = this.safeTimestamp (response, 'timestamp'); return this.parseOrderBook (response, timestamp); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const type = 'limit'; const side = this.safeString (trade, 'type'); const id = this.safeString (trade, 'tid'); return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetGetBalance (params); const result = { 'info': response }; const balances = this.safeValue (response, 'funds'); const currencies = Object.keys (balances); for (let i = 0; i < currencies.length; i++) { const currencyId = currencies[i]; const balance = balances[currencyId]; const code = this.safeCurrencyCode (currencyId); const account = { 'free': this.safeFloat (balance, 'balance'), 'used': this.safeFloat (balance, 'freeze'), 'total': this.safeFloat (balance, 'total'), }; result[code] = account; } return this.parseBalance (result); } parseOrder (order, market = undefined) { // // { // "fees": 0, // "total_amount": 1, // "trade_amount": 0, // "price": 30, // "currency": “eth_hsr", // "id": "13878", // "trade_money": 0, // "type": "buy", // "trade_date": 1509728897772, // "status": 0 // } // const symbol = market['symbol']; const timestamp = parseInt (order['trade_date']); const price = this.safeFloat (order, 'price'); const cost = this.safeFloat (order, 'trade_money'); const amount = this.safeFloat (order, 'total_amount'); const filled = this.safeFloat (order, 'trade_amount', 0.0); const remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); let status = this.safeInteger (order, 'status'); if (status === 1) { status = 'canceled'; } else if (status === 2) { status = 'closed'; } else { status = 'open'; } let fee = undefined; if ('fees' in order) { fee = { 'cost': this.safeFloat (order, 'fees'), 'currency': market['quote'], }; } return { 'id': this.safeString (order, 'id'), 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': 'limit', 'side': order['type'], 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': fee, 'info': order, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], 'type': side, 'price': price, 'amount': amount, }; const response = await this.privateGetOrder (this.extend (request, params)); const id = this.safeString (response, 'id'); const order = this.parseOrder ({ 'id': id, 'trade_date': this.milliseconds (), 'total_amount': amount, 'price': price, 'type': side, 'info': response, }, market); this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': id, 'currency': market['id'], }; const response = await this.privateGetCancel (this.extend (request, params)); return response; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': id, 'currency': market['id'], }; const response = await this.privateGetGetOrder (this.extend (request, params)); return this.parseOrder (response, market); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], }; const response = await this.privateGetGetOpenOrders (this.extend (request, params)); if (!Array.isArray (response)) { return []; } return this.parseOrders (response, market, since, limit); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); const query = this.urlencode (this.keysort (this.extend ({ 'accesskey': this.apiKey, 'nonce': this.nonce (), }, params))); const signed = this.hmac (this.encode (query), this.encode (this.secret), 'sha512'); url += '?' + query + '&signature=' + signed; headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // // {"result":false,"message":"服务端忙碌"} // ... and other formats // const code = this.safeString (response, 'code'); const message = this.safeString (response, 'message'); const feedback = this.id + ' ' + body; if (code === '100') { return; } if (code !== undefined) { this.throwExactlyMatchedException (this.exceptions, code, feedback); if (code === '308') { // this is returned by the exchange when there are no open orders // {"code":308,"message":"Not Found Transaction Record"} return; } else { throw new ExchangeError (feedback); } } const result = this.safeValue (response, 'result'); if (result !== undefined) { if (!result) { if (message === '服务端忙碌') { throw new ExchangeNotAvailable (feedback); } else { throw new ExchangeError (feedback); } } } } }; },{"./base/Exchange":7,"./base/errors":9}],81:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadSymbol, ExchangeError, ExchangeNotAvailable, ArgumentsRequired, InsufficientFunds, InvalidOrder, RateLimitExceeded, InvalidNonce, AuthenticationError, NotSupported } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class fcoin extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'fcoin', 'name': 'FCoin', 'countries': [ 'CN' ], 'rateLimit': 2000, 'userAgent': this.userAgents['chrome39'], 'version': 'v2', 'accounts': undefined, 'accountsById': undefined, 'hostname': 'fcoin.com', 'has': { 'CORS': false, 'fetchDepositAddress': false, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOrderBook': true, 'fetchOrderBooks': false, 'fetchTradingLimits': false, 'withdraw': false, 'fetchCurrencies': false, }, 'timeframes': { '1m': 'M1', '3m': 'M3', '5m': 'M5', '15m': 'M15', '30m': 'M30', '1h': 'H1', '1d': 'D1', '1w': 'W1', '1M': 'MN', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/42244210-c8c42e1e-7f1c-11e8-8710-a5fb63b165c4.jpg', 'api': { 'public': 'https://api.{hostname}', 'private': 'https://api.{hostname}', 'market': 'https://api.{hostname}', 'openapi': 'https://www.{hostname}', }, 'www': 'https://www.fcoin.com', 'referral': 'https://www.fcoin.com/i/Z5P7V', 'doc': 'https://developer.fcoin.com', 'fees': 'https://fcoinjp.zendesk.com/hc/en-us/articles/360018727371', }, 'api': { 'openapi': { 'get': [ 'symbols', ], }, 'market': { 'get': [ 'ticker/{symbol}', 'depth/{level}/{symbol}', 'trades/{symbol}', 'candles/{timeframe}/{symbol}', ], }, 'public': { 'get': [ 'symbols', 'currencies', 'server-time', ], }, 'private': { 'get': [ 'accounts/balance', 'assets/accounts/balance', 'broker/otc/suborders', 'broker/otc/suborders/{id}', 'broker/otc/suborders/{id}/payments', 'broker/otc/users', 'broker/otc/users/me/balances', 'broker/otc/users/me/balance', 'broker/leveraged_accounts/account', 'broker/leveraged_accounts', 'orders', 'orders/{order_id}', 'orders/{order_id}/match-results', // check order result ], 'post': [ 'assets/accounts/assets-to-spot', 'accounts/spot-to-assets', 'broker/otc/assets/transfer/in', 'broker/otc/assets/transfer/out', 'broker/otc/suborders', 'broker/otc/suborders/{id}/pay_confirm', 'broker/otc/suborders/{id}/cancel', 'broker/leveraged/assets/transfer/in', 'broker/leveraged/assets/transfer/out', 'orders', 'orders/{order_id}/submit-cancel', // cancel order ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': -0.0002, 'taker': 0.0003, }, }, 'limits': { 'amount': { 'min': 0.01, 'max': 100000 }, }, 'options': { 'createMarketBuyOrderRequiresPrice': true, 'fetchMarketsMethod': 'fetch_markets_from_open_api', // or 'fetch_markets_from_api' 'limits': { 'BTM/USDT': { 'amount': { 'min': 0.1, 'max': 10000000 }}, 'ETC/USDT': { 'amount': { 'min': 0.001, 'max': 400000 }}, 'ETH/USDT': { 'amount': { 'min': 0.001, 'max': 10000 }}, 'LTC/USDT': { 'amount': { 'min': 0.001, 'max': 40000 }}, 'BCH/USDT': { 'amount': { 'min': 0.001, 'max': 5000 }}, 'BTC/USDT': { 'amount': { 'min': 0.001, 'max': 1000 }}, 'ICX/ETH': { 'amount': { 'min': 0.01, 'max': 3000000 }}, 'OMG/ETH': { 'amount': { 'min': 0.01, 'max': 500000 }}, 'FT/USDT': { 'amount': { 'min': 1, 'max': 10000000 }}, 'ZIL/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, 'ZIP/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, 'FT/BTC': { 'amount': { 'min': 1, 'max': 10000000 }}, 'FT/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, }, }, 'exceptions': { '400': NotSupported, // Bad Request '401': AuthenticationError, '405': NotSupported, '429': RateLimitExceeded, // Too Many Requests, exceed api request limit '1002': ExchangeNotAvailable, // System busy '1016': InsufficientFunds, '2136': AuthenticationError, // The API key is expired '3008': InvalidOrder, '6004': InvalidNonce, '6005': AuthenticationError, // Illegal API Signature '40003': BadSymbol, }, 'commonCurrencies': { 'DAG': 'DAGX', 'PAI': 'PCHAIN', 'MT': 'Mariana Token', }, }); } async fetchMarkets (params = {}) { const method = this.safeString (this.options, 'fetchMarketsMethod', 'fetch_markets_from_open_api'); return await this[method] (params); } async fetchMarketsFromOpenAPI (params = {}) { // https://github.com/ccxt/ccxt/issues/5648 const response = await this.openapiGetSymbols (params); // // { // "status":"ok", // "data":{ // "categories":[ "fone::coinforce", ... ], // "symbols":{ // "mdaeth":{ // "price_decimal":8, // "amount_decimal":2, // "base_currency":"mda", // "quote_currency":"eth", // "symbol":"mdaeth", // "category":"fone::bitangel", // "leveraged_multiple":null, // "tradeable":false, // "market_order_enabled":false, // "limit_amount_min":"1", // "limit_amount_max":"10000000", // "main_tag":"", // "daily_open_at":"", // "daily_close_at":"" // }, // } // "category_ref":{ // "fone::coinforce":[ "btcusdt", ... ], // } // } // } // const data = this.safeValue (response, 'data', {}); const markets = this.safeValue (data, 'symbols', {}); const keys = Object.keys (markets); const result = []; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const market = markets[key]; const id = this.safeString (market, 'symbol'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'price': this.safeInteger (market, 'price_decimal'), 'amount': this.safeInteger (market, 'amount_decimal'), }; const limits = { 'amount': { 'min': this.safeFloat (market, 'limit_amount_min'), 'max': this.safeFloat (market, 'limit_amount_max'), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': undefined, 'max': undefined, }, }; const active = this.safeValue (market, 'tradeable', false); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchMarketsFromAPI (params = {}) { const response = await this.publicGetSymbols (params); // // { // "status":0, // "data":[ // { // "name":"dapusdt", // "base_currency":"dap", // "quote_currency":"usdt", // "price_decimal":6, // "amount_decimal":2, // "tradable":true // }, // ] // } // const result = []; const markets = this.safeValue (response, 'data'); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'name'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'price': market['price_decimal'], 'amount': market['amount_decimal'], }; let limits = { 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, }; const active = this.safeValue (market, 'tradable', false); if (symbol in this.options['limits']) { limits = this.extend (this.options['limits'][symbol], limits); } result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccountsBalance (params); const result = { 'info': response }; const balances = this.safeValue (response, 'data'); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['total'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'frozen'); result[code] = account; } return this.parseBalance (result); } parseBidsAsks (orders, priceKey = 0, amountKey = 1) { const result = []; const length = orders.length; const halfLength = parseInt (length / 2); // += 2 in the for loop below won't transpile for (let i = 0; i < halfLength; i++) { const index = i * 2; const priceField = this.sum (index, priceKey); const amountField = this.sum (index, amountKey); result.push ([ this.safeFloat (orders, priceField), this.safeFloat (orders, amountField), ]); } return result; } async fetchOrderBook (symbol = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (limit !== undefined) { if ((limit === 20) || (limit === 150)) { limit = 'L' + limit.toString (); } else { throw new ExchangeError (this.id + ' fetchOrderBook supports limit of 20 or 150. Other values are not accepted'); } } else { limit = 'L20'; } const request = { 'symbol': this.marketId (symbol), 'level': limit, // L20, L150 }; const response = await this.marketGetDepthLevelSymbol (this.extend (request, params)); const orderbook = this.safeValue (response, 'data'); return this.parseOrderBook (orderbook, orderbook['ts'], 'bids', 'asks', 0, 1); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const ticker = await this.marketGetTickerSymbol (this.extend (request, params)); return this.parseTicker (ticker['data'], market); } parseTicker (ticker, market = undefined) { const timestamp = undefined; let symbol = undefined; if (market === undefined) { const tickerType = this.safeString (ticker, 'type'); if (tickerType !== undefined) { const parts = tickerType.split ('.'); const id = parts[1]; if (id in this.markets_by_id) { market = this.markets_by_id[id]; } } } const values = ticker['ticker']; const last = this.safeFloat (values, 0); if (market !== undefined) { symbol = market['symbol']; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (values, 7), 'low': this.safeFloat (values, 8), 'bid': this.safeFloat (values, 2), 'bidVolume': this.safeFloat (values, 3), 'ask': this.safeFloat (values, 4), 'askVolume': this.safeFloat (values, 5), 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (values, 9), 'quoteVolume': this.safeFloat (values, 10), 'info': ticker, }; } parseTrade (trade, market = undefined) { let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger (trade, 'ts'); const side = this.safeStringLower (trade, 'side'); const id = this.safeString (trade, 'id'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } const fee = undefined; return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'order': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'limit': limit, }; if (since !== undefined) { request['timestamp'] = parseInt (since / 1000); } const response = await this.marketGetTradesSymbol (this.extend (request, params)); return this.parseTrades (response['data'], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), 'side': side, 'type': type, }; // for market buy it requires the amount of quote currency to spend if ((type === 'market') && (side === 'buy')) { if (this.options['createMarketBuyOrderRequiresPrice']) { if (price === undefined) { throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); } else { request['amount'] = this.costToPrecision (symbol, amount * price); } } else { request['amount'] = this.costToPrecision (symbol, amount); } } else { request['amount'] = this.amountToPrecision (symbol, amount); } if ((type === 'limit') || (type === 'ioc') || (type === 'fok')) { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostOrders (this.extend (request, params)); return { 'info': response, 'id': response['data'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privatePostOrdersOrderIdSubmitCancel (this.extend (request, params)); const order = this.parseOrder (response); return this.extend (order, { 'id': id, 'status': 'canceled', }); } parseOrderStatus (status) { const statuses = { 'submitted': 'open', 'canceled': 'canceled', 'partial_filled': 'open', 'partial_canceled': 'canceled', 'filled': 'closed', 'pending_cancel': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "id": "string", // "symbol": "string", // "type": "limit", // "side": "buy", // "price": "string", // "amount": "string", // "state": "submitted", // "executed_value": "string", // "fill_fees": "string", // "filled_amount": "string", // "created_at": 0, // "source": "web" // } // const id = this.safeString (order, 'id'); const side = this.safeString (order, 'side'); const status = this.parseOrderStatus (this.safeString (order, 'state')); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } const orderType = this.safeString (order, 'type'); const timestamp = this.safeInteger (order, 'created_at'); const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'filled_amount'); let remaining = undefined; let price = this.safeFloat (order, 'price'); let cost = this.safeFloat (order, 'executed_value'); if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; } if (cost === undefined) { if (price !== undefined) { cost = price * filled; } } else if ((cost > 0) && (filled > 0)) { price = cost / filled; } } let feeCurrency = undefined; let feeCost = undefined; const feeRebate = this.safeFloat (order, 'fees_income'); if ((feeRebate !== undefined) && (feeRebate > 0)) { if (market !== undefined) { symbol = market['symbol']; feeCurrency = (side === 'buy') ? market['quote'] : market['base']; } feeCost = -feeRebate; } else { feeCost = this.safeFloat (order, 'fill_fees'); if (market !== undefined) { symbol = market['symbol']; feeCurrency = (side === 'buy') ? market['base'] : market['quote']; } } return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': orderType, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'remaining': remaining, 'filled': filled, 'average': undefined, 'status': status, 'fee': { 'cost': feeCost, 'currency': feeCurrency, }, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privateGetOrdersOrderId (this.extend (request, params)); return this.parseOrder (response['data']); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'states': 'submitted,partial_filled' }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'states': 'partial_canceled,filled' }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders() requires a `symbol` argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'states': 'submitted,partial_filled,partial_canceled,filled,canceled', }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetOrders (this.extend (request, params)); return this.parseOrders (response['data'], market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ this.safeTimestamp (ohlcv, 'id'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'base_vol'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 20; // default is 20 } const request = { 'symbol': market['id'], 'timeframe': this.timeframes[timeframe], 'limit': limit, }; if (since !== undefined) { const sinceInSeconds = parseInt (since / 1000); const timerange = limit * this.parseTimeframe (timeframe); request['before'] = this.sum (sinceInSeconds, timerange) - 1; } const response = await this.marketGetCandlesTimeframeSymbol (this.extend (request, params)); return this.parseOHLCVs (response['data'], market, timeframe, since, limit); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/'; const openAPI = (api === 'openapi'); const privateAPI = (api === 'private'); request += openAPI ? (api + '/') : ''; request += this.version + '/'; request += (privateAPI || openAPI) ? '' : (api + '/'); request += this.implodeParams (path, params); let query = this.omit (params, this.extractParams (path)); let url = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname, }); url += request; if (privateAPI) { this.checkRequiredCredentials (); const timestamp = this.nonce ().toString (); query = this.keysort (query); if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.rawencode (query); } } // HTTP_METHOD + HTTP_REQUEST_URI + TIMESTAMP + POST_BODY let auth = method + url + timestamp; if (method === 'POST') { if (Object.keys (query).length) { body = this.json (query); auth += this.urlencode (query); } } const payload = this.stringToBase64 (this.encode (auth)); let signature = this.hmac (payload, this.encode (this.secret), 'sha1', 'binary'); signature = this.decode (this.stringToBase64 (signature)); headers = { 'FC-ACCESS-KEY': this.apiKey, 'FC-ACCESS-SIGNATURE': signature, 'FC-ACCESS-TIMESTAMP': timestamp, 'Content-Type': 'application/json', }; } else { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } const status = this.safeString (response, 'status'); if (status !== '0' && status !== 'ok') { const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, status, feedback); throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9}],82:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const fcoin = require ('./fcoin.js'); // --------------------------------------------------------------------------- module.exports = class fcoinjp extends fcoin { describe () { return this.deepExtend (super.describe (), { 'id': 'fcoinjp', 'name': 'FCoinJP', 'countries': [ 'JP' ], 'hostname': 'fcoinjp.com', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/54219174-08b66b00-4500-11e9-862d-f522d0fe08c6.jpg', 'fees': 'https://fcoinjp.zendesk.com/hc/en-us/articles/360018727371', 'www': 'https://www.fcoinjp.com', 'referral': undefined, }, }); } }; },{"./fcoin.js":81}],83:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class flowbtc extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'flowbtc', 'name': 'flowBTC', 'countries': [ 'BR' ], // Brazil 'version': 'v1', 'rateLimit': 1000, 'has': { 'CORS': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/28162465-cd815d4c-67cf-11e7-8e57-438bea0523a2.jpg', 'api': 'https://publicapi.flowbtc.com.br', 'www': 'https://www.flowbtc.com.br', 'doc': 'https://www.flowbtc.com.br/api.html', }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': true, }, 'api': { 'public': { 'post': [ 'GetTicker', 'GetTrades', 'GetTradesByDate', 'GetOrderBook', 'GetProductPairs', 'GetProducts', ], }, 'private': { 'post': [ 'CreateAccount', 'GetUserInfo', 'SetUserInfo', 'GetAccountInfo', 'GetAccountTrades', 'GetDepositAddresses', 'Withdraw', 'CreateOrder', 'ModifyOrder', 'CancelOrder', 'CancelAllOrders', 'GetAccountOpenOrders', 'GetOrderFee', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.0025, 'taker': 0.005, }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicPostGetProductPairs (params); const markets = this.safeValue (response, 'productPairs'); const result = {}; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'name'); const baseId = this.safeString (market, 'product1Label'); const quoteId = this.safeString (market, 'product2Label'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const precision = { 'amount': this.safeInteger (market, 'product1DecimalPlaces'), 'price': this.safeInteger (market, 'product2DecimalPlaces'), }; const symbol = base + '/' + quote; result[symbol] = { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, 'active': undefined, }; } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGetAccountInfo (params); const balances = this.safeValue (response, 'currencies'); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = balance['name']; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'balance'); account['total'] = this.safeFloat (balance, 'hold'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'productPair': market['id'], }; const response = await this.publicPostGetOrderBook (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'bids', 'asks', 'px', 'qty'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'productPair': market['id'], }; const ticker = await this.publicPostGetTicker (this.extend (request, params)); const timestamp = this.milliseconds (); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume24hr'), 'quoteVolume': this.safeFloat (ticker, 'volume24hrProduct2'), 'info': ticker, }; } parseTrade (trade, market) { const timestamp = this.safeTimestamp (trade, 'unixtime'); const side = (trade['incomingOrderSide'] === 0) ? 'buy' : 'sell'; const id = this.safeString (trade, 'tid'); const price = this.safeFloat (trade, 'px'); const amount = this.safeFloat (trade, 'qty'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': market['symbol'], 'id': id, 'order': undefined, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'takerOrMaker': undefined, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'ins': market['id'], 'startIndex': -1, }; const response = await this.publicPostGetTrades (this.extend (request, params)); return this.parseTrades (response['trades'], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const orderType = (type === 'market') ? 1 : 0; const request = { 'ins': this.marketId (symbol), 'side': side, 'orderType': orderType, 'qty': amount, 'px': this.priceToPrecision (symbol, price), }; const response = await this.privatePostCreateOrder (this.extend (request, params)); return { 'info': response, 'id': response['serverOrderId'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); if ('ins' in params) { const request = { 'serverOrderId': id, }; return await this.privatePostCancelOrder (this.extend (request, params)); } throw new ExchangeError (this.id + ' requires `ins` symbol parameter for cancelling an order'); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const url = this.urls['api'] + '/' + this.version + '/' + path; if (api === 'public') { if (Object.keys (params).length) { body = this.json (params); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); const auth = nonce.toString () + this.uid + this.apiKey; const signature = this.hmac (this.encode (auth), this.encode (this.secret)); body = this.json (this.extend ({ 'apiKey': this.apiKey, 'apiNonce': nonce, 'apiSig': signature.toUpperCase (), }, params)); headers = { 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('isAccepted' in response) { if (response['isAccepted']) { return response; } } throw new ExchangeError (this.id + ' ' + this.json (response)); } }; },{"./base/Exchange":7,"./base/errors":9}],84:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class foxbit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'foxbit', 'name': 'FoxBit', 'countries': [ 'BR' ], 'has': { 'CORS': false, 'createMarketOrder': false, }, 'rateLimit': 1000, 'version': 'v1', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27991413-11b40d42-647f-11e7-91ee-78ced874dd09.jpg', 'api': { 'public': 'https://api.blinktrade.com/api', 'private': 'https://api.blinktrade.com/tapi', }, 'www': 'https://foxbit.com.br/exchange', 'doc': 'https://foxbit.com.br/api/', }, 'comment': 'Blinktrade API', 'api': { 'public': { 'get': [ '{currency}/ticker', // ?crypto_currency=BTC '{currency}/orderbook', // ?crypto_currency=BTC '{currency}/trades', // ?crypto_currency=BTC&since=&limit= ], }, 'private': { 'post': [ 'D', // order 'F', // cancel order 'U2', // balance 'U4', // my orders 'U6', // withdraw 'U18', // deposit 'U24', // confirm withdrawal 'U26', // list withdrawals 'U30', // list deposits 'U34', // ledger 'U70', // cancel withdrawal ], }, }, 'markets': { 'BTC/VEF': { 'id': 'BTCVEF', 'symbol': 'BTC/VEF', 'base': 'BTC', 'quote': 'VEF', 'brokerId': 1, 'broker': 'SurBitcoin' }, 'BTC/VND': { 'id': 'BTCVND', 'symbol': 'BTC/VND', 'base': 'BTC', 'quote': 'VND', 'brokerId': 3, 'broker': 'VBTC' }, 'BTC/BRL': { 'id': 'BTCBRL', 'symbol': 'BTC/BRL', 'base': 'BTC', 'quote': 'BRL', 'brokerId': 4, 'broker': 'FoxBit' }, 'BTC/PKR': { 'id': 'BTCPKR', 'symbol': 'BTC/PKR', 'base': 'BTC', 'quote': 'PKR', 'brokerId': 8, 'broker': 'UrduBit' }, 'BTC/CLP': { 'id': 'BTCCLP', 'symbol': 'BTC/CLP', 'base': 'BTC', 'quote': 'CLP', 'brokerId': 9, 'broker': 'ChileBit' }, }, 'options': { 'brokerId': '4', // https://blinktrade.com/docs/#brokers }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const request = { 'BalanceReqID': this.nonce (), }; const response = await this.privatePostU2 (this.extend (request, params)); const balances = this.safeValue (response['Responses'], this.options['brokerId']); const result = { 'info': response }; if (balances !== undefined) { const currencyIds = Object.keys (this.currencies_by_id); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); // we only set the balance for the currency if that currency is present in response // otherwise we will lose the info if the currency balance has been funded or traded or not if (currencyId in balances) { const account = this.account (); let used = this.safeFloat (balances, currencyId + '_locked'); if (used !== undefined) { used *= 1e-8; } let total = this.safeFloat (balances, currencyId); if (total !== undefined) { total *= 1e-8; } account['used'] = used; account['total'] = total; result[code] = account; } } } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['quote'], 'crypto_currency': market['base'], }; const response = await this.publicGetCurrencyOrderbook (this.extend (request, params)); return this.parseOrderBook (response); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['quote'], 'crypto_currency': market['base'], }; const ticker = await this.publicGetCurrencyTicker (this.extend (request, params)); const timestamp = this.milliseconds (); const lowercaseQuote = market['quote'].toLowerCase (); const quoteVolume = 'vol_' + lowercaseQuote; const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, quoteVolume), 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const id = this.safeString (trade, 'tid'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const side = this.safeString (trade, 'side'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['quote'], 'crypto_currency': market['base'], }; const response = await this.publicGetCurrencyTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } const market = this.market (symbol); const orderSide = (side === 'buy') ? '1' : '2'; const request = { 'ClOrdID': this.nonce (), 'Symbol': market['id'], 'Side': orderSide, 'OrdType': '2', 'Price': price, 'OrderQty': amount, 'BrokerID': market['brokerId'], }; const response = await this.privatePostD (this.extend (request, params)); const indexed = this.indexBy (response['Responses'], 'MsgType'); const execution = indexed['8']; return { 'info': response, 'id': execution['OrderID'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); return await this.privatePostF (this.extend ({ 'ClOrdID': id, }, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const request = this.extend ({ 'MsgType': path }, query); body = this.json (request); headers = { 'APIKey': this.apiKey, 'Nonce': nonce, 'Signature': this.hmac (this.encode (nonce), this.encode (this.secret)), 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('Status' in response) { if (response['Status'] !== 200) { throw new ExchangeError (this.id + ' ' + this.json (response)); } } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],85:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { TICK_SIZE } = require ('./base/functions/number'); const { ExchangeError, InvalidOrder, BadRequest, InsufficientFunds, OrderNotFound } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class ftx extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'ftx', 'name': 'FTX', 'countries': [ 'HK' ], 'rateLimit': 100, 'certified': true, 'pro': true, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg', 'www': 'https://ftx.com', 'api': { 'public': 'https://ftx.com', 'private': 'https://ftx.com', }, 'doc': 'https://github.com/ftexchange/ftx', 'fees': 'https://ftexchange.zendesk.com/hc/en-us/articles/360024479432-Fees', 'referral': 'https://ftx.com/#a=1623029', }, 'has': { 'cancelAllOrders': true, 'fetchClosedOrders': false, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFees': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFees': true, 'fetchWithdrawals': true, 'withdraw': true, }, 'timeframes': { '15s': '15', '1m': '60', '5m': '300', '15m': '900', '1h': '3600', '4h': '14400', '1d': '86400', }, 'api': { 'public': { 'get': [ 'coins', 'markets', 'markets/{market_name}', 'markets/{market_name}/orderbook', // ?depth={depth} 'markets/{market_name}/trades', // ?limit={limit}&start_time={start_time}&end_time={end_time} 'markets/{market_name}/candles', // ?resolution={resolution}&limit={limit}&start_time={start_time}&end_time={end_time} 'futures', 'futures/{future_name}', 'futures/{future_name}/stats', 'funding_rates', 'lt/tokens', 'lt/{token_name}', ], }, 'private': { 'get': [ 'account', 'positions', 'wallet/coins', 'wallet/balances', 'wallet/deposit_address/{coin}', 'wallet/deposits', 'wallet/withdrawals', 'orders', // ?market={market} 'orders/history', // ?market={market} 'orders/{order_id}', 'orders/by_client_id/{client_order_id}', 'conditional_orders', // ?market={market} 'conditional_orders/history', // ?market={market} 'fills', // ?market={market} 'funding_payments', 'lt/balances', 'lt/creations', 'lt/redemptions', 'subaccounts', 'subaccounts/{nickname}/balances', ], 'post': [ 'account/leverage', 'wallet/withdrawals', 'orders', 'conditional_orders', 'lt/{token_name}/create', 'lt/{token_name}/redeem', 'subaccounts', 'subaccounts/update_name', 'subaccounts/transfer', ], 'delete': [ 'orders/{order_id}', 'orders/by_client_id/{client_order_id}', 'orders', 'conditional_orders/{order_id}', 'subaccounts', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'maker': 0.02 / 100, 'taker': 0.07 / 100, 'tiers': { 'taker': [ [0, 0.07 / 100], [1000000, 0.06 / 100], [5000000, 0.055 / 100], [10000000, 0.05 / 100], [15000000, 0.045 / 100], [35000000, 0.04 / 100], ], 'maker': [ [0, 0.02 / 100], [1000000, 0.02 / 100], [5000000, 0.015 / 100], [10000000, 0.015 / 100], [15000000, 0.01 / 100], [35000000, 0.01 / 100], ], }, }, 'funding': { 'withdraw': {}, }, }, 'exceptions': { 'exact': { 'Not enough balances': InsufficientFunds, // {"error":"Not enough balances","success":false} 'InvalidPrice': InvalidOrder, // {"error":"Invalid price","success":false} 'Size too small': InvalidOrder, // {"error":"Size too small","success":false} 'Missing parameter price': InvalidOrder, // {"error":"Missing parameter price","success":false} 'Order not found': OrderNotFound, // {"error":"Order not found","success":false} }, 'broad': { 'Invalid parameter': BadRequest, // {"error":"Invalid parameter start_time","success":false} 'The requested URL was not found on the server': BadRequest, 'No such coin': BadRequest, 'No such market': BadRequest, 'An unexpected error occurred': ExchangeError, // {"error":"An unexpected error occurred, please try again later (58BC21C795).","success":false} }, }, 'precisionMode': TICK_SIZE, 'options': { // support for canceling conditional orders // https://github.com/ccxt/ccxt/issues/6669 'cancelOrder': { 'method': 'privateDeleteOrdersOrderId', // privateDeleteConditionalOrdersOrderId }, 'fetchOpenOrders': { 'method': 'privateGetOrders', // privateGetConditionalOrders }, 'fetchOrders': { 'method': 'privateGetOrdersHistory', // privateGetConditionalOrdersHistory }, }, }); } async fetchCurrencies (params = {}) { const response = await this.publicGetCoins (params); const currencies = this.safeValue (response, 'result', []); // // { // "success":true, // "result": [ // {"id":"BTC","name":"Bitcoin"}, // {"id":"ETH","name":"Ethereum"}, // {"id":"ETHMOON","name":"10X Long Ethereum Token","underlying":"ETH"}, // {"id":"EOSBULL","name":"3X Long EOS Token","underlying":"EOS"}, // ], // } // const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'id'); const code = this.safeCurrencyCode (id); const name = this.safeString (currency, 'name'); result[code] = { 'id': id, 'code': code, 'info': currency, 'type': undefined, 'name': name, 'active': undefined, 'fee': undefined, 'precision': undefined, 'limits': { 'withdraw': { 'min': undefined, 'max': undefined }, 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, }, }; } return result; } async fetchMarkets (params = {}) { const response = await this.publicGetMarkets (params); // // { // 'success': true, // "result": [ // { // "ask":170.37, // "baseCurrency":null, // "bid":170.31, // "change1h":-0.019001554672655036, // "change24h":-0.024841165359738997, // "changeBod":-0.03816406029469881, // "enabled":true, // "last":170.37, // "name":"ETH-PERP", // "price":170.37, // "priceIncrement":0.01, // "quoteCurrency":null, // "quoteVolume24h":7742164.59889, // "sizeIncrement":0.001, // "type":"future", // "underlying":"ETH", // "volumeUsd24h":7742164.59889 // }, // { // "ask":170.44, // "baseCurrency":"ETH", // "bid":170.41, // "change1h":-0.018485459257126403, // "change24h":-0.023825887743413515, // "changeBod":-0.037605872388481086, // "enabled":true, // "last":172.72, // "name":"ETH/USD", // "price":170.44, // "priceIncrement":0.01, // "quoteCurrency":"USD", // "quoteVolume24h":382802.0252, // "sizeIncrement":0.001, // "type":"spot", // "underlying":null, // "volumeUsd24h":382802.0252 // }, // ], // } // const result = []; const markets = this.safeValue (response, 'result', []); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'name'); const baseId = this.safeString2 (market, 'baseCurrency', 'underlying'); const quoteId = this.safeString (market, 'quoteCurrency', 'USD'); const type = this.safeString (market, 'type'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); // check if a market is a spot or future market const symbol = (type === 'future') ? this.safeString (market, 'name') : (base + '/' + quote); const active = this.safeValue (market, 'enabled'); const sizeIncrement = this.safeFloat (market, 'sizeIncrement'); const priceIncrement = this.safeFloat (market, 'priceIncrement'); const precision = { 'amount': sizeIncrement, 'price': priceIncrement, }; const entry = { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'type': type, 'future': (type === 'future'), 'spot': (type === 'spot'), 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': sizeIncrement, 'max': undefined, }, 'price': { 'min': priceIncrement, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }; result.push (entry); } return result; } parseTicker (ticker, market = undefined) { // // { // "ask":171.29, // "baseCurrency":null, // base currency for spot markets // "bid":171.24, // "change1h":-0.0012244897959183673, // "change24h":-0.031603346901854366, // "changeBod":-0.03297013492914808, // "enabled":true, // "last":171.44, // "name":"ETH-PERP", // "price":171.29, // "priceIncrement":0.01, // "quoteCurrency":null, // quote currency for spot markets // "quoteVolume24h":8570651.12113, // "sizeIncrement":0.001, // "type":"future", // "underlying":"ETH", // null for spot markets // "volumeUsd24h":8570651.12113, // } // let symbol = undefined; const marketId = this.safeString (ticker, 'name'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const type = this.safeString (ticker, 'type'); if (type === 'future') { symbol = marketId; } else { const base = this.safeCurrencyCode (this.safeString (ticker, 'baseCurrency')); const quote = this.safeCurrencyCode (this.safeString (ticker, 'quoteCurrency')); if ((base !== undefined) && (quote !== undefined)) { symbol = base + '/' + quote; } } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); const timestamp = this.safeTimestamp (ticker, 'time', this.milliseconds ()); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': this.safeFloat (ticker, 'bidSize'), 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': this.safeFloat (ticker, 'askSize'), 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': this.safeFloat (ticker, 'change24h'), 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeFloat (ticker, 'quoteVolume24h'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_name': market['id'], }; const response = await this.publicGetMarketsMarketName (this.extend (request, params)); // // { // "success":true, // "result":{ // "ask":171.29, // "baseCurrency":null, // base currency for spot markets // "bid":171.24, // "change1h":-0.0012244897959183673, // "change24h":-0.031603346901854366, // "changeBod":-0.03297013492914808, // "enabled":true, // "last":171.44, // "name":"ETH-PERP", // "price":171.29, // "priceIncrement":0.01, // "quoteCurrency":null, // quote currency for spot markets // "quoteVolume24h":8570651.12113, // "sizeIncrement":0.001, // "type":"future", // "underlying":"ETH", // null for spot markets // "volumeUsd24h":8570651.12113, // } // } // const result = this.safeValue (response, 'result', {}); return this.parseTicker (result, market); } parseTickers (tickers, symbols = undefined) { const result = []; for (let i = 0; i < tickers.length; i++) { result.push (this.parseTicker (tickers[i])); } return this.filterByArray (result, 'symbol', symbols); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarkets (params); // // { // 'success': true, // "result": [ // { // "ask":170.44, // "baseCurrency":"ETH", // "bid":170.41, // "change1h":-0.018485459257126403, // "change24h":-0.023825887743413515, // "changeBod":-0.037605872388481086, // "enabled":true, // "last":172.72, // "name":"ETH/USD", // "price":170.44, // "priceIncrement":0.01, // "quoteCurrency":"USD", // "quoteVolume24h":382802.0252, // "sizeIncrement":0.001, // "type":"spot", // "underlying":null, // "volumeUsd24h":382802.0252 // }, // ], // } // const tickers = this.safeValue (response, 'result', []); return this.parseTickers (tickers, symbols); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_name': market['id'], }; if (limit !== undefined) { request['depth'] = limit; // max 100, default 20 } const response = await this.publicGetMarketsMarketNameOrderbook (this.extend (request, params)); // // { // "success":true, // "result":{ // "asks":[ // [171.95,279.865], // [171.98,102.42], // [171.99,124.11], // ], // "bids":[ // [171.93,69.749], // [171.9,288.325], // [171.88,87.47], // ], // } // } // const result = this.safeValue (response, 'result', {}); return this.parseOrderBook (result); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // { // "close":177.23, // "high":177.45, // "low":177.2, // "open":177.43, // "startTime":"2019-10-17T13:27:00+00:00", // "time":1571318820000.0, // "volume":0.0 // } // return [ this.safeInteger (ohlcv, 'time'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_name': market['id'], 'resolution': this.timeframes[timeframe], }; // max 1501 candles, including the current candle when since is not specified limit = (limit === undefined) ? 1501 : limit; if (since === undefined) { request['end_time'] = this.seconds (); request['limit'] = limit; request['start_time'] = request['end_time'] - limit * this.parseTimeframe (timeframe); } else { request['start_time'] = parseInt (since / 1000); request['limit'] = limit; request['end_time'] = this.sum (request['start_time'], limit * this.parseTimeframe (timeframe)); } const response = await this.publicGetMarketsMarketNameCandles (this.extend (request, params)); // // { // "success": true, // "result":[ // { // "close":177.23, // "high":177.45, // "low":177.2, // "open":177.43, // "startTime":"2019-10-17T13:27:00+00:00", // "time":1571318820000.0, // "volume":0.0 // }, // { // "close":177.26, // "high":177.33, // "low":177.23, // "open":177.23, // "startTime":"2019-10-17T13:28:00+00:00", // "time":1571318880000.0, // "volume":0.0 // }, // ], // } // const result = this.safeValue (response, 'result', []); return this.parseOHLCVs (result, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "id":1715826, // "liquidation":false, // "price":171.62, // "side":"buy", // "size":2.095, // "time":"2019-10-18T12:59:54.288166+00:00" // } // // fetchMyTrades (private) // // { // "fee": 20.1374935, // "feeRate": 0.0005, // "feeCurrency": "USD", // "future": "EOS-0329", // "id": 11215, // "liquidity": "taker", // "market": "EOS-0329", // "baseCurrency": null, // "quoteCurrency": null, // "orderId": 8436981, // "price": 4.201, // "side": "buy", // "size": 9587, // "time": "2019-03-27T19:15:10.204619+00:00", // "type": "order" // } // const id = this.safeString (trade, 'id'); const takerOrMaker = this.safeString (trade, 'liquidity'); const marketId = this.safeString (trade, 'market'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const base = this.safeCurrencyCode (this.safeString (trade, 'baseCurrency')); const quote = this.safeCurrencyCode (this.safeString (trade, 'quoteCurrency')); if ((base !== undefined) && (quote !== undefined)) { symbol = base + '/' + quote; } else { symbol = marketId; } } } const timestamp = this.parse8601 (this.safeString (trade, 'time')); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'size'); if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const side = this.safeString (trade, 'side'); let cost = undefined; if (price !== undefined && amount !== undefined) { cost = price * amount; } let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'feeCurrency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, 'rate': this.safeFloat (trade, 'feeRate'), }; } const orderId = this.safeString (trade, 'orderId'); return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': undefined, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market_name': market['id'], }; if (since !== undefined) { request['start_time'] = parseInt (since / 1000); // start_time doesn't work without end_time request['end_time'] = this.seconds (); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetMarketsMarketNameTrades (this.extend (request, params)); // // { // "success":true, // "result":[ // { // "id":1715826, // "liquidation":false, // "price":171.62, // "side":"buy", // "size":2.095, // "time":"2019-10-18T12:59:54.288166+00:00" // }, // { // "id":1715763, // "liquidation":false, // "price":171.89, // "side":"sell", // "size":1.477, // "time":"2019-10-18T12:58:38.443734+00:00" // }, // ], // } // const result = this.safeValue (response, 'result', []); return this.parseTrades (result, market, since, limit); } async fetchTradingFees (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccount (params); // // { // "success": true, // "result": { // "backstopProvider": true, // "collateral": 3568181.02691129, // "freeCollateral": 1786071.456884368, // "initialMarginRequirement": 0.12222384240257728, // "liquidating": false, // "maintenanceMarginRequirement": 0.07177992558058484, // "makerFee": 0.0002, // "marginFraction": 0.5588433331419503, // "openMarginFraction": 0.2447194090423075, // "takerFee": 0.0005, // "totalAccountValue": 3568180.98341129, // "totalPositionSize": 6384939.6992, // "username": "user@domain.com", // "positions": [ // { // "cost": -31.7906, // "entryPrice": 138.22, // "future": "ETH-PERP", // "initialMarginRequirement": 0.1, // "longOrderSize": 1744.55, // "maintenanceMarginRequirement": 0.04, // "netSize": -0.23, // "openSize": 1744.32, // "realizedPnl": 3.39441714, // "shortOrderSize": 1732.09, // "side": "sell", // "size": 0.23, // "unrealizedPnl": 0, // }, // ], // }, // } // const result = this.safeValue (response, 'result', {}); return { 'info': response, 'maker': this.safeFloat (result, 'makerFee'), 'taker': this.safeFloat (result, 'takerFee'), }; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetWalletBalances (params); // // { // "success": true, // "result": [ // { // "coin": "USDTBEAR", // "free": 2320.2, // "total": 2340.2 // }, // ], // } // const result = { 'info': response, }; const balances = this.safeValue (response, 'result', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const code = this.safeCurrencyCode (this.safeString (balance, 'coin')); const account = this.account (); account['free'] = this.safeFloat (balance, 'free'); account['total'] = this.safeFloat (balance, 'total'); result[code] = account; } return this.parseBalance (result); } parseOrderStatus (status) { const statuses = { 'new': 'open', 'open': 'open', 'closed': 'closed', // filled or canceled }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // limit orders - fetchOrder, fetchOrders, fetchOpenOrders, createOrder // // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "filledSize": 0, // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "price": 0.306525, // "remainingSize": 31431, // "side": "sell", // "size": 31431, // "status": "open", // "type": "limit", // "reduceOnly": false, // "ioc": false, // "postOnly": false, // "clientId": null, // } // // market orders - fetchOrder, fetchOrders, fetchOpenOrders, createOrder // // { // "avgFillPrice": 2666.0, // "clientId": None, // "createdAt": "2020-02-12T00: 53: 49.009726+00: 00", // "filledSize": 0.0007, // "future": None, // "id": 3109208514, // "ioc": True, // "market": "BNBBULL/USD", // "postOnly": False, // "price": None, // "reduceOnly": False, // "remainingSize": 0.0, // "side": "buy", // "size": 0.0007, // "status": "closed", // "type": "market" // } // // createOrder (conditional, "stop", "trailingStop", or "takeProfit") // // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "triggerPrice": 0.306525, // "orderId": null, // "side": "sell", // "size": 31431, // "status": "open", // "type": "stop", // "orderPrice": null, // "error": null, // "triggeredAt": null, // "reduceOnly": false // } // const id = this.safeString (order, 'id'); const timestamp = this.parse8601 (this.safeString (order, 'createdAt')); const filled = this.safeFloat (order, 'filledSize'); const remaining = this.safeFloat (order, 'remainingSize'); let symbol = undefined; const marketId = this.safeString (order, 'market'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const status = this.parseOrderStatus (this.safeString (order, 'status')); const side = this.safeString (order, 'side'); const type = this.safeString (order, 'type'); const amount = this.safeFloat (order, 'size'); const average = this.safeFloat (order, 'avgFillPrice'); const price = this.safeFloat2 (order, 'price', 'triggerPrice', average); let cost = undefined; if (filled !== undefined && price !== undefined) { cost = filled * price; } const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'triggeredAt')); const clientOrderId = this.safeString (order, 'clientId'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': average, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'side': side, // "buy" or "sell" // 'price': 0.306525, // send null for market orders 'type': type, // "limit", "market", "stop", "trailingStop", or "takeProfit" 'size': parseFloat (this.amountToPrecision (symbol, amount)), // 'reduceOnly': false, // optional, default is false // 'ioc': false, // optional, default is false, limit or market orders only // 'postOnly': false, // optional, default is false, limit or market orders only // 'clientId': 'abcdef0123456789', // string, optional, client order id, limit or market orders only }; let priceToPrecision = undefined; if (price !== undefined) { priceToPrecision = parseFloat (this.priceToPrecision (symbol, price)); } let method = 'privatePostConditionalOrders'; if (type === 'limit') { method = 'privatePostOrders'; request['price'] = priceToPrecision; } else if (type === 'market') { method = 'privatePostOrders'; request['price'] = null; } else if ((type === 'stop') || (type === 'takeProfit')) { request['triggerPrice'] = priceToPrecision; // request['orderPrice'] = number; // optional, order type is limit if this is specified, otherwise market } else if (type === 'trailingStop') { request['trailValue'] = priceToPrecision; // negative for "sell", positive for "buy" } else { throw new InvalidOrder (this.id + ' createOrder () does not support order type ' + type + ', only limit, market, stop, trailingStop, or takeProfit orders are supported'); } const response = await this[method] (this.extend (request, params)); // // orders // // { // "success": true, // "result": [ // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "filledSize": 0, // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "price": 0.306525, // "remainingSize": 31431, // "side": "sell", // "size": 31431, // "status": "open", // "type": "limit", // "reduceOnly": false, // "ioc": false, // "postOnly": false, // "clientId": null, // } // ] // } // // conditional orders // // { // "success": true, // "result": [ // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "triggerPrice": 0.306525, // "orderId": null, // "side": "sell", // "size": 31431, // "status": "open", // "type": "stop", // "orderPrice": null, // "error": null, // "triggeredAt": null, // "reduceOnly": false // } // ] // } // // const result = this.safeValue (response, 'result', []); return this.parseOrder (result, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': parseInt (id), }; // support for canceling conditional orders // https://github.com/ccxt/ccxt/issues/6669 const options = this.safeValue (this.options, 'cancelOrder', {}); const defaultMethod = this.safeString (options, 'method', 'privateDeleteOrdersOrderId'); let method = this.safeString (params, 'method', defaultMethod); const type = this.safeValue (params, 'type'); if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) { method = 'privateDeleteConditionalOrdersOrderId'; } const query = this.omit (params, [ 'method', 'type' ]); const response = await this[method] (this.extend (request, query)); // // { // "success": true, // "result": "Order queued for cancelation" // } // const result = this.safeValue (response, 'result', {}); return result; } async cancelAllOrders (symbol = undefined, params = {}) { await this.loadMarkets (); const request = { // 'market': market['id'], // optional 'conditionalOrdersOnly': false, // cancel conditional orders only 'limitOrdersOnly': false, // cancel existing limit orders (non-conditional orders) only }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const response = await this.privateDeleteOrders (this.extend (request, params)); const result = this.safeValue (response, 'result', {}); // // { // "success": true, // "result": "Orders queued for cancelation" // } // return result; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privateGetOrdersOrderId (this.extend (request, params)); // // { // "success": true, // "result": { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "filledSize": 10, // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "price": 0.306525, // "avgFillPrice": 0.306526, // "remainingSize": 31421, // "side": "sell", // "size": 31431, // "status": "open", // "type": "limit", // "reduceOnly": false, // "ioc": false, // "postOnly": false, // "clientId": null // } // } // const result = this.safeValue (response, 'result', {}); return this.parseOrder (result); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } // support for canceling conditional orders // https://github.com/ccxt/ccxt/issues/6669 const options = this.safeValue (this.options, 'fetchOpenOrders', {}); const defaultMethod = this.safeString (options, 'method', 'privateGetOrders'); let method = this.safeString (params, 'method', defaultMethod); const type = this.safeValue (params, 'type'); if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) { method = 'privateGetConditionalOrders'; } const query = this.omit (params, [ 'method', 'type' ]); const response = await this[method] (this.extend (request, query)); // // { // "success": true, // "result": [ // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "filledSize": 10, // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "price": 0.306525, // "avgFillPrice": 0.306526, // "remainingSize": 31421, // "side": "sell", // "size": 31431, // "status": "open", // "type": "limit", // "reduceOnly": false, // "ioc": false, // "postOnly": false, // "clientId": null // } // ] // } // const result = this.safeValue (response, 'result', []); return this.parseOrders (result, market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; // default 100, max 100 } if (since !== undefined) { request['start_time'] = parseInt (since / 1000); } // support for canceling conditional orders // https://github.com/ccxt/ccxt/issues/6669 const options = this.safeValue (this.options, 'fetchOrders', {}); const defaultMethod = this.safeString (options, 'method', 'privateGetOrdersHistory'); let method = this.safeString (params, 'method', defaultMethod); const type = this.safeValue (params, 'type'); if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) { method = 'privateGetConditionalOrdersHistory'; } const query = this.omit (params, [ 'method', 'type' ]); const response = await this[method] (this.extend (request, query)); // // { // "success": true, // "result": [ // { // "createdAt": "2019-03-05T09:56:55.728933+00:00", // "filledSize": 10, // "future": "XRP-PERP", // "id": 9596912, // "market": "XRP-PERP", // "price": 0.306525, // "avgFillPrice": 0.306526, // "remainingSize": 31421, // "side": "sell", // "size": 31431, // "status": "open", // "type": "limit", // "reduceOnly": false, // "ioc": false, // "postOnly": false, // "clientId": null // } // ] // } // const result = this.safeValue (response, 'result', []); return this.parseOrders (result, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['start_time'] = parseInt (since / 1000); } const response = await this.privateGetFills (this.extend (request, params)); // // { // "success": true, // "result": [ // { // "fee": 20.1374935, // "feeRate": 0.0005, // "future": "EOS-0329", // "id": 11215, // "liquidity": "taker", // "market": "EOS-0329", // "baseCurrency": null, // "quoteCurrency": null, // "orderId": 8436981, // "price": 4.201, // "side": "buy", // "size": 9587, // "time": "2019-03-27T19:15:10.204619+00:00", // "type": "order" // } // ] // } // const trades = this.safeValue (response, 'result', []); return this.parseTrades (trades, market, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { await this.loadMarkets (); this.checkAddress (address); const currency = this.currency (code); const request = { 'coin': currency['id'], 'size': amount, 'address': address, // 'password': 'string', // optional withdrawal password if it is required for your account // 'code': '192837', // optional 2fa code if it is required for your account }; if (tag !== undefined) { request['tag'] = tag; } const response = await this.privatePostWalletWithdrawals (this.extend (request, params)); // // { // "success": true, // "result": { // "coin": "USDTBEAR", // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", // "tag": "null", // "fee": 0, // "id": 1, // "size": "20.2", // "status": "requested", // "time": "2019-03-05T09:56:55.728933+00:00", // "txid": "null" // } // } // const result = this.safeValue (response, 'result', {}); return this.parseTransaction (result, currency); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'coin': currency['id'], }; const response = await this.privateGetWalletDepositAddressCoin (this.extend (request, params)); // // { // "success": true, // "result": { // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", // "tag": "null" // } // } // const result = this.safeValue (response, 'result', {}); const address = this.safeString (result, 'address'); const tag = this.safeString (result, 'tag'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } parseTransactionStatus (status) { const statuses = { // what are other statuses here? 'confirmed': 'ok', // deposits 'complete': 'ok', // withdrawals }; return this.safeString (statuses, status, status); } parseTransaction (transaction) { // // fetchDeposits // // { // "coin": "TUSD", // "confirmations": 64, // "confirmedTime": "2019-03-05T09:56:55.728933+00:00", // "fee": 0, // "id": 1, // "sentTime": "2019-03-05T09:56:55.735929+00:00", // "size": "99.0", // "status": "confirmed", // "time": "2019-03-05T09:56:55.728933+00:00", // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" // } // // fetchWithdrawals // // { // "coin": "TUSD", // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", // "tag": "null", // "fee": 0, // "id": 1, // "size": "99.0", // "status": "complete", // "time": "2019-03-05T09:56:55.728933+00:00", // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" // } // const code = this.safeCurrencyCode (this.safeString (transaction, 'coin')); const id = this.safeInteger (transaction, 'id'); const amount = this.safeFloat (transaction, 'size'); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const timestamp = this.parse8601 (this.safeString (transaction, 'time')); const txid = this.safeString (transaction, 'txid'); const address = this.safeString (transaction, 'address'); const tag = this.safeString (transaction, 'tag'); const fee = this.safeFloat (transaction, 'fee'); return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'addressFrom': undefined, 'address': undefined, 'addressTo': address, 'tagFrom': undefined, 'tag': tag, 'tagTo': undefined, 'type': undefined, 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': { 'currency': code, 'cost': fee, 'rate': undefined, }, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.privateGetWalletDeposits (params); // // { // "success": true, // "result": { // "coin": "TUSD", // "confirmations": 64, // "confirmedTime": "2019-03-05T09:56:55.728933+00:00", // "fee": 0, // "id": 1, // "sentTime": "2019-03-05T09:56:55.735929+00:00", // "size": "99.0", // "status": "confirmed", // "time": "2019-03-05T09:56:55.728933+00:00", // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" // } // } // const result = this.safeValue (response, 'result', []); return this.parseTransactions (result); } async fetchWithdrawals (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.privateGetWalletWithdrawals (params); // // { // "success": true, // "result": { // "coin": "TUSD", // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", // "tag": "null", // "fee": 0, // "id": 1, // "size": "99.0", // "status": "complete", // "time": "2019-03-05T09:56:55.728933+00:00", // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" // } // } // const result = this.safeValue (response, 'result', []); return this.parseTransactions (result); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let request = '/api/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'][api] + request; if (method !== 'POST') { if (Object.keys (query).length) { const suffix = '?' + this.urlencode (query); url += suffix; request += suffix; } } if (api === 'private') { this.checkRequiredCredentials (); const timestamp = this.milliseconds ().toString (); let auth = timestamp + method + request; headers = { 'FTX-KEY': this.apiKey, 'FTX-TS': timestamp, }; if (method === 'POST') { body = this.json (query); auth += body; headers['Content-Type'] = 'application/json'; } const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256'); headers['FTX-SIGN'] = signature; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to the default error handler } // // {"error":"Invalid parameter start_time","success":false} // {"error":"Not enough balances","success":false} // const success = this.safeValue (response, 'success'); if (!success) { const feedback = this.id + ' ' + body; const error = this.safeString (response, 'error'); this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],86:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class fybse extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'fybse', 'name': 'FYB-SE', 'countries': [ 'SE' ], // Sweden 'has': { 'CORS': false, }, 'rateLimit': 1500, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766512-31019772-5edb-11e7-8241-2e675e6797f1.jpg', 'api': 'https://www.fybse.se/api/SEK', 'www': 'https://www.fybse.se', 'doc': 'https://fyb.docs.apiary.io', }, 'api': { 'public': { 'get': [ 'ticker', 'tickerdetailed', 'orderbook', 'trades', ], }, 'private': { 'post': [ 'test', 'getaccinfo', 'getpendingorders', 'getorderhistory', 'cancelpendingorder', 'placeorder', 'withdraw', ], }, }, 'markets': { 'BTC/SEK': { 'id': 'SEK', 'symbol': 'BTC/SEK', 'base': 'BTC', 'quote': 'SEK' }, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGetaccinfo (params); const btc = this.safeFloat (response, 'btcBal'); const symbol = this.symbols[0]; const quote = this.markets[symbol]['quote']; const lowercase = quote.toLowerCase () + 'Bal'; const fiat = this.safeFloat (response, lowercase); const crypto = this.account (); crypto['total'] = btc; const result = { 'BTC': crypto }; result[quote] = this.account (); result[quote]['total'] = fiat; result['info'] = response; return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetOrderbook (params); return this.parseOrderBook (response); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const ticker = await this.publicGetTickerdetailed (params); const timestamp = this.milliseconds (); const last = this.safeFloat (ticker, 'last'); const volume = this.safeFloat (ticker, 'vol'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': volume, 'quoteVolume': undefined, 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const id = this.safeString (trade, 'tid'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } return { 'id': id, 'info': trade, 'order': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const response = await this.publicGetTrades (params); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'qty': amount, 'price': price, 'type': side[0].toUpperCase (), }; const response = await this.privatePostPlaceorder (this.extend (request, params)); return { 'info': response, 'id': response['pending_oid'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderNo': id, }; return await this.privatePostCancelpendingorder (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + path; if (api === 'public') { url += '.json'; } else { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'timestamp': nonce }, params)); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'key': this.apiKey, 'sig': this.hmac (this.encode (body), this.encode (this.secret), 'sha1'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if (api === 'private') { if ('error' in response) { if (response['error']) { throw new ExchangeError (this.id + ' ' + this.json (response)); } } } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],87:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { AuthenticationError, ExchangeError, ArgumentsRequired, InvalidAddress, OrderNotFound, NotSupported, DDoSProtection, InsufficientFunds, InvalidOrder } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class gateio extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'gateio', 'name': 'Gate.io', 'countries': [ 'CN' ], 'version': '2', 'rateLimit': 1000, 'pro': true, 'has': { 'CORS': false, 'createMarketOrder': false, 'fetchTickers': true, 'withdraw': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTransactions': true, 'createDepositAddress': true, 'fetchDepositAddress': true, 'fetchClosedOrders': false, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrderTrades': true, 'fetchOrders': true, 'fetchOrder': true, 'fetchMyTrades': true, }, 'timeframes': { '1m': 60, '5m': 300, '10m': 600, '15m': 900, '30m': 1800, '1h': 3600, '2h': 7200, '4h': 14400, '6h': 21600, '12h': 43200, '1d': 86400, '1w': 604800, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg', 'api': { 'public': 'https://data.gate.io/api', 'private': 'https://data.gate.io/api', }, 'www': 'https://gate.io/', 'doc': 'https://gate.io/api2', 'fees': [ 'https://gate.io/fee', 'https://support.gate.io/hc/en-us/articles/115003577673', ], 'referral': 'https://www.gate.io/signup/2436035', }, 'api': { 'public': { 'get': [ 'candlestick2/{id}', 'pairs', 'marketinfo', 'marketlist', 'coininfo', 'tickers', 'ticker/{id}', 'orderBook/{id}', 'trade/{id}', 'tradeHistory/{id}', 'tradeHistory/{id}/{tid}', ], }, 'private': { 'post': [ 'balances', 'depositAddress', 'newAddress', 'depositsWithdrawals', 'buy', 'sell', 'cancelOrder', 'cancelAllOrders', 'getOrder', 'openOrders', 'tradeHistory', 'feelist', 'withdraw', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'maker': 0.002, 'taker': 0.002, }, }, 'exceptions': { 'exact': { '4': DDoSProtection, '5': AuthenticationError, // { result: "false", code: 5, message: "Error: invalid key or sign, please re-generate it from your account" } '6': AuthenticationError, // { result: 'false', code: 6, message: 'Error: invalid data ' } '7': NotSupported, '8': NotSupported, '9': NotSupported, '15': DDoSProtection, '16': OrderNotFound, '17': OrderNotFound, '20': InvalidOrder, '21': InsufficientFunds, }, // https://gate.io/api2#errCode 'errorCodeNames': { '1': 'Invalid request', '2': 'Invalid version', '3': 'Invalid request', '4': 'Too many attempts', '5': 'Invalid sign', '6': 'Invalid sign', '7': 'Currency is not supported', '8': 'Currency is not supported', '9': 'Currency is not supported', '10': 'Verified failed', '11': 'Obtaining address failed', '12': 'Empty params', '13': 'Internal error, please report to administrator', '14': 'Invalid user', '15': 'Cancel order too fast, please wait 1 min and try again', '16': 'Invalid order id or order is already closed', '17': 'Invalid orderid', '18': 'Invalid amount', '19': 'Not permitted or trade is disabled', '20': 'Your order size is too small', '21': 'You don\'t have enough fund', }, }, 'options': { 'fetchTradesMethod': 'public_get_tradehistory_id', // 'public_get_tradehistory_id_tid' 'limits': { 'cost': { 'min': { 'BTC': 0.0001, 'ETH': 0.001, 'USDT': 1, }, }, }, }, 'commonCurrencies': { 'BTCBEAR': 'BEAR', 'BTCBULL': 'BULL', }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetMarketinfo (params); // // { // "result":"true", // "pairs":[ // { // "usdt_cnyx":{ // "decimal_places":3, // "amount_decimal_places":3, // "min_amount":1, // "min_amount_a":1, // "min_amount_b":3, // "fee":0.02, // "trade_disabled":0, // "buy_disabled":0, // "sell_disabled":0 // } // }, // ] // } // const markets = this.safeValue (response, 'pairs'); if (!markets) { throw new ExchangeError (this.id + ' fetchMarkets got an unrecognized response'); } const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const keys = Object.keys (market); const id = this.safeString (keys, 0); const details = market[id]; // all of their symbols are separated with an underscore // but not boe_eth_eth (BOE_ETH/ETH) which has two underscores // https://github.com/ccxt/ccxt/issues/4894 const parts = id.split ('_'); const numParts = parts.length; let baseId = parts[0]; let quoteId = parts[1]; if (numParts > 2) { baseId = parts[0] + '_' + parts[1]; quoteId = parts[2]; } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': 8, 'price': this.safeInteger (details, 'decimal_places'), }; const amountLimits = { 'min': this.safeFloat (details, 'min_amount'), 'max': undefined, }; const priceLimits = { 'min': Math.pow (10, -precision['price']), 'max': undefined, }; const defaultCost = amountLimits['min'] * priceLimits['min']; const minCost = this.safeFloat (this.options['limits']['cost']['min'], quote, defaultCost); const costLimits = { 'min': minCost, 'max': undefined, }; const limits = { 'amount': amountLimits, 'price': priceLimits, 'cost': costLimits, }; const disabled = this.safeValue (details, 'trade_disabled'); const active = !disabled; const uppercaseId = id.toUpperCase (); const fee = this.safeFloat (details, 'fee'); result.push ({ 'id': id, 'uppercaseId': uppercaseId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': active, 'maker': fee / 100, 'taker': fee / 100, 'precision': precision, 'limits': limits, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostBalances (params); const result = { 'info': response }; let available = this.safeValue (response, 'available', {}); if (Array.isArray (available)) { available = {}; } const locked = this.safeValue (response, 'locked', {}); const currencyIds = Object.keys (available); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (available, currencyId); account['used'] = this.safeFloat (locked, currencyId); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const response = await this.publicGetOrderBookId (this.extend (request, params)); return this.parseOrderBook (response); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // they return [ Timestamp, Volume, Close, High, Low, Open ] return [ parseInt (ohlcv[0]), // t parseFloat (ohlcv[5]), // o parseFloat (ohlcv[3]), // h parseFloat (ohlcv[4]), // l parseFloat (ohlcv[2]), // c parseFloat (ohlcv[1]), // v ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], 'group_sec': this.timeframes[timeframe], }; // max limit = 1001 if (limit !== undefined) { const periodDurationInSeconds = this.parseTimeframe (timeframe); const hours = parseInt ((periodDurationInSeconds * limit) / 3600); request['range_hour'] = Math.max (0, hours - 1); } const response = await this.publicGetCandlestick2Id (this.extend (request, params)); // // { // "elapsed": "15ms", // "result": "true", // "data": [ // [ "1553930820000", "1.005299", "4081.05", "4086.18", "4081.05", "4086.18" ], // [ "1553930880000", "0.110923277", "4095.2", "4095.23", "4091.15", "4091.15" ], // ... // [ "1553934420000", "0", "4089.42", "4089.42", "4089.42", "4089.42" ], // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOHLCVs (data, market, timeframe, since, limit); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); const percentage = this.safeFloat (ticker, 'percentChange'); let open = undefined; let change = undefined; let average = undefined; if ((last !== undefined) && (percentage !== undefined)) { const relativeChange = percentage / 100; open = last / this.sum (1, relativeChange); change = last - open; average = this.sum (last, open) / 2; } open = this.safeFloat (ticker, 'open', open); change = this.safeFloat (ticker, 'change', change); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat2 (ticker, 'high24hr', 'high'), 'low': this.safeFloat2 (ticker, 'low24hr', 'low'), 'bid': this.safeFloat (ticker, 'highestBid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'lowestAsk'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': this.safeFloat (ticker, 'quoteVolume'), // gateio has them reversed 'quoteVolume': this.safeFloat (ticker, 'baseVolume'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickers (params); const result = {}; const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const [ baseId, quoteId ] = id.split ('_'); let base = baseId.toUpperCase (); let quote = quoteId.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const symbol = base + '/' + quote; let market = undefined; if (symbol in this.markets) { market = this.markets[symbol]; } if (id in this.markets_by_id) { market = this.markets_by_id[id]; } result[symbol] = this.parseTicker (response[id], market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const ticker = await this.publicGetTickerId (this.extend ({ 'id': market['id'], }, params)); return this.parseTicker (ticker, market); } parseTrade (trade, market = undefined) { // { // "tradeID": 3175762, // "date": "2017-08-25 07:24:28", // "type": "sell", // "rate": 29011, // "amount": 0.0019, // "total": 55.1209, // "fee": "0", // "fee_coin": "btc", // "gt_fee":"0", // "point_fee":"0.1213", // }, let timestamp = this.safeTimestamp2 (trade, 'timestamp', 'time_unix'); timestamp = this.safeTimestamp (trade, 'time', timestamp); const id = this.safeString2 (trade, 'tradeID', 'id'); // take either of orderid or orderId const orderId = this.safeString2 (trade, 'orderid', 'orderNumber'); const price = this.safeFloat2 (trade, 'rate', 'price'); const amount = this.safeFloat (trade, 'amount'); const type = this.safeString (trade, 'type'); const takerOrMaker = this.safeString (trade, 'role'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } let fee = undefined; let feeCurrency = this.safeCurrencyCode (this.safeString (trade, 'fee_coin')); let feeCost = this.safeFloat (trade, 'point_fee'); if ((feeCost === undefined) || (feeCost === 0)) { feeCost = this.safeFloat (trade, 'gt_fee'); if ((feeCost === undefined) || (feeCost === 0)) { feeCost = this.safeFloat (trade, 'fee'); } else { feeCurrency = this.safeCurrencyCode ('GT'); } } else { feeCurrency = this.safeCurrencyCode ('POINT'); } if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': feeCurrency, }; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': type, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; const method = this.safeString (this.options, 'fetchTradesMethod', 'public_get_tradehistory_id'); const response = await this[method] (this.extend (request, params)); return this.parseTrades (response['data'], market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const response = await this.privatePostOpenOrders (params); return this.parseOrders (response['orders'], undefined, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderNumber': id, 'currencyPair': this.marketId (symbol), }; const response = await this.privatePostGetOrder (this.extend (request, params)); return this.parseOrder (response['order']); } parseOrderStatus (status) { const statuses = { 'cancelled': 'canceled', // 'closed': 'closed', // these two statuses aren't actually needed // 'open': 'open', // as they are mapped one-to-one }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "fee": "0 ZEC", // "code": 0, // "rate": "0.0055", // "side": 2, // "type": "buy", // "ctime": 1586460839.138, // "market": "ZEC_BTC", // "result": "true", // "status": "open", // "iceberg": "0", // "message": "Success", // "feeValue": "0", // "filledRate": "0.005500000", // "leftAmount": "0.60607456", // "feeCurrency": "ZEC", // "orderNumber": 10755887009, // "filledAmount": "0", // "feePercentage": 0.002, // "initialAmount": "0.60607456" // } // // { // 'amount': '0.00000000', // 'currencyPair': 'xlm_usdt', // 'fee': '0.0113766632239302 USDT', // 'feeCurrency': 'USDT', // 'feePercentage': 0.18, // 'feeValue': '0.0113766632239302', // 'filledAmount': '30.14004987', // 'filledRate': 0.2097, // 'initialAmount': '30.14004987', // 'initialRate': '0.2097', // 'left': 0, // 'orderNumber': '998307286', // 'rate': '0.2097', // 'status': 'closed', // 'timestamp': 1531158583, // 'type': 'sell' // } // // { // "orderNumber": 10802237760, // "orderType": 1, // "type": "buy", // "rate": "0.54250000", // "amount": "45.55638518", // "total": "24.71433896", // "initialRate": "0.54250000", // "initialAmount": "45.55638518", // "filledRate": "0.54250000", // "filledAmount": "0", // "currencyPair": "nano_usdt", // "timestamp": 1586556143, // "status": "open" // } // const id = this.safeString2 (order, 'orderNumber', 'id'); let symbol = undefined; const marketId = this.safeString (order, 'currencyPair'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeTimestamp2 (order, 'timestamp', 'ctime'); const lastTradeTimestamp = this.safeTimestamp (order, 'mtime'); const status = this.parseOrderStatus (this.safeString (order, 'status')); let side = this.safeString (order, 'type'); // handling for order.update messages if (side === '1') { side = 'sell'; } else if (side === '2') { side = 'buy'; } const price = this.safeFloat2 (order, 'initialRate', 'rate'); const average = this.safeFloat (order, 'filledRate'); const amount = this.safeFloat2 (order, 'initialAmount', 'amount'); const filled = this.safeFloat (order, 'filledAmount'); // In the order status response, this field has a different name. let remaining = this.safeFloat2 (order, 'leftAmount', 'left'); if (remaining === undefined) { remaining = amount - filled; } const feeCost = this.safeFloat (order, 'feeValue'); const feeCurrencyId = this.safeString (order, 'feeCurrency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); let feeRate = this.safeFloat (order, 'feePercentage'); if (feeRate !== undefined) { feeRate = feeRate / 100; } return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': lastTradeTimestamp, 'status': status, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': undefined, 'amount': amount, 'filled': filled, 'remaining': remaining, 'average': average, 'trades': undefined, 'fee': { 'cost': feeCost, 'currency': feeCurrencyCode, 'rate': feeRate, }, 'info': order, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const method = 'privatePost' + this.capitalize (side); const market = this.market (symbol); const request = { 'currencyPair': market['id'], 'rate': price, 'amount': amount, }; const response = await this[method] (this.extend (request, params)); return this.parseOrder (this.extend ({ 'status': 'open', 'type': side, 'initialAmount': amount, }, response), market); } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires symbol argument'); } await this.loadMarkets (); const request = { 'orderNumber': id, 'currencyPair': this.marketId (symbol), }; return await this.privatePostCancelOrder (this.extend (request, params)); } async queryDepositAddress (method, code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); method = 'privatePost' + method + 'Address'; const request = { 'currency': currency['id'], }; const response = await this[method] (this.extend (request, params)); let address = this.safeString (response, 'addr'); let tag = undefined; if ((address !== undefined) && (address.indexOf ('address') >= 0)) { throw new InvalidAddress (this.id + ' queryDepositAddress ' + address); } if (code === 'XRP') { const parts = address.split (' '); address = parts[0]; tag = parts[1]; } return { 'currency': currency, 'address': address, 'tag': tag, 'info': response, }; } async createDepositAddress (code, params = {}) { return await this.queryDepositAddress ('New', code, params); } async fetchDepositAddress (code, params = {}) { return await this.queryDepositAddress ('Deposit', code, params); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const response = await this.privatePostOpenOrders (params); return this.parseOrders (response['orders'], market, since, limit); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPair': market['id'], 'orderNumber': id, }; const response = await this.privatePostTradeHistory (this.extend (request, params)); return this.parseTrades (response['trades'], market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPair': market['id'], }; const response = await this.privatePostTradeHistory (this.extend (request, params)); return this.parseTrades (response['trades'], market, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'amount': amount, 'address': address, // Address must exist in you AddressBook in security settings }; if (tag !== undefined) { request['address'] += ' ' + tag; } const response = await this.privatePostWithdraw (this.extend (request, params)); return { 'info': response, 'id': undefined, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const prefix = (api === 'private') ? (api + '/') : ''; let url = this.urls['api'][api] + this.version + '/1/' + prefix + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); const request = { 'nonce': nonce }; body = this.urlencode (this.extend (request, query)); const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); headers = { 'Key': this.apiKey, 'Sign': signature, 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async fetchTransactionsByType (type = undefined, code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (since !== undefined) { request['start'] = since; } const response = await this.privatePostDepositsWithdrawals (this.extend (request, params)); let transactions = undefined; if (type === undefined) { const deposits = this.safeValue (response, 'deposits', []); const withdrawals = this.safeValue (response, 'withdraws', []); transactions = this.arrayConcat (deposits, withdrawals); } else { transactions = this.safeValue (response, type, []); } let currency = undefined; if (code !== undefined) { currency = this.currency (code); } return this.parseTransactions (transactions, currency, since, limit); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchTransactionsByType (undefined, code, since, limit, params); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchTransactionsByType ('deposits', code, since, limit, params); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchTransactionsByType ('withdraws', code, since, limit, params); } parseTransaction (transaction, currency = undefined) { // // deposit // // { // 'id': 'd16520849', // 'currency': 'NEO', // 'address': False, // 'amount': '1', // 'txid': '01acf6b8ce4d24a....', // 'timestamp': '1553125968', // 'status': 'DONE', // 'type': 'deposit' // } // // withdrawal // // { // 'id': 'w5864259', // 'currency': 'ETH', // 'address': '0x72632f462....', // 'amount': '0.4947', // 'txid': '0x111167d120f736....', // 'timestamp': '1553123688', // 'status': 'DONE', // 'type': 'withdrawal' // } // const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const id = this.safeString (transaction, 'id'); const txid = this.safeString (transaction, 'txid'); const amount = this.safeFloat (transaction, 'amount'); let address = this.safeString (transaction, 'address'); if (address === 'false') { address = undefined; } const timestamp = this.safeTimestamp (transaction, 'timestamp'); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const type = this.parseTransactionType (id[0]); return { 'info': transaction, 'id': id, 'txid': txid, 'currency': code, 'amount': amount, 'address': address, 'tag': undefined, 'status': status, 'type': type, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': undefined, }; } parseTransactionStatus (status) { const statuses = { 'PEND': 'pending', 'REQUEST': 'pending', 'DMOVE': 'pending', 'CANCEL': 'failed', 'DONE': 'ok', }; return this.safeString (statuses, status, status); } parseTransactionType (type) { const types = { 'd': 'deposit', 'w': 'withdrawal', }; return this.safeString (types, type, type); } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } const resultString = this.safeString (response, 'result', ''); if (resultString !== 'false') { return; } const errorCode = this.safeString (response, 'code'); const message = this.safeString (response, 'message', body); if (errorCode !== undefined) { const feedback = this.safeString (this.exceptions['errorCodeNames'], errorCode, message); this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); } } }; },{"./base/Exchange":7,"./base/errors":9}],88:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, BadRequest, OrderNotFound, InvalidOrder, InvalidNonce, InsufficientFunds, AuthenticationError, PermissionDenied, NotSupported, OnMaintenance, RateLimitExceeded } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class gemini extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'gemini', 'name': 'Gemini', 'countries': [ 'US' ], 'rateLimit': 1500, // 200 for private API 'version': 'v1', 'has': { 'fetchDepositAddress': false, 'createDepositAddress': true, 'CORS': false, 'fetchBidsAsks': false, 'fetchTickers': false, 'fetchMyTrades': true, 'fetchOrder': true, 'fetchOrders': false, 'fetchOpenOrders': true, 'fetchClosedOrders': false, 'createMarketOrder': false, 'withdraw': true, 'fetchTransactions': true, 'fetchWithdrawals': false, 'fetchDeposits': false, 'fetchOHLCV': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27816857-ce7be644-6096-11e7-82d6-3c257263229c.jpg', 'api': { 'public': 'https://api.gemini.com', 'private': 'https://api.gemini.com', 'web': 'https://docs.gemini.com', }, 'www': 'https://gemini.com/', 'doc': [ 'https://docs.gemini.com/rest-api', 'https://docs.sandbox.gemini.com', ], 'test': { 'public': 'https://api.sandbox.gemini.com', 'private': 'https://api.sandbox.gemini.com', 'web': 'https://docs.sandbox.gemini.com', }, 'fees': [ 'https://gemini.com/api-fee-schedule', 'https://gemini.com/trading-fees', 'https://gemini.com/transfer-fees', ], }, 'api': { 'web': { 'get': [ 'rest-api', ], }, 'public': { 'get': [ 'v1/symbols', 'v1/pubticker/{symbol}', 'v1/book/{symbol}', 'v1/trades/{symbol}', 'v1/auction/{symbol}', 'v1/auction/{symbol}/history', 'v2/candles/{symbol}/{timeframe}', 'v2/ticker/{symbol}', ], }, 'private': { 'post': [ 'v1/order/new', 'v1/order/cancel', 'v1/order/cancel/session', 'v1/order/cancel/all', 'v1/order/status', 'v1/orders', 'v1/mytrades', 'v1/notionalvolume', 'v1/tradevolume', 'v1/transfers', 'v1/balances', 'v1/deposit/{currency}/newAddress', 'v1/withdraw/{currency}', 'v1/heartbeat', 'v1/transfers', ], }, }, 'fees': { 'trading': { 'taker': 0.0035, 'maker': 0.001, }, }, 'httpExceptions': { '400': BadRequest, // Auction not open or paused, ineligible timing, market not open, or the request was malformed, in the case of a private API request, missing or malformed Gemini private API authentication headers '403': PermissionDenied, // The API key is missing the role necessary to access this private API endpoint '404': OrderNotFound, // Unknown API entry point or Order not found '406': InsufficientFunds, // Insufficient Funds '429': RateLimitExceeded, // Rate Limiting was applied '500': ExchangeError, // The server encountered an error '502': ExchangeError, // Technical issues are preventing the request from being satisfied '503': OnMaintenance, // The exchange is down for maintenance }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1hr', '6h': '6hr', '1d': '1day', }, 'exceptions': { 'exact': { 'AuctionNotOpen': BadRequest, // Failed to place an auction-only order because there is no current auction open for this symbol 'ClientOrderIdTooLong': BadRequest, // The Client Order ID must be under 100 characters 'ClientOrderIdMustBeString': BadRequest, // The Client Order ID must be a string 'ConflictingOptions': BadRequest, // New orders using a combination of order execution options are not supported 'EndpointMismatch': BadRequest, // The request was submitted to an endpoint different than the one in the payload 'EndpointNotFound': BadRequest, // No endpoint was specified 'IneligibleTiming': BadRequest, // Failed to place an auction order for the current auction on this symbol because the timing is not eligible, new orders may only be placed before the auction begins. 'InsufficientFunds': InsufficientFunds, // The order was rejected because of insufficient funds 'InvalidJson': BadRequest, // The JSON provided is invalid 'InvalidNonce': InvalidNonce, // The nonce was not greater than the previously used nonce, or was not present 'InvalidOrderType': InvalidOrder, // An unknown order type was provided 'InvalidPrice': InvalidOrder, // For new orders, the price was invalid 'InvalidQuantity': InvalidOrder, // A negative or otherwise invalid quantity was specified 'InvalidSide': InvalidOrder, // For new orders, and invalid side was specified 'InvalidSignature': AuthenticationError, // The signature did not match the expected signature 'InvalidSymbol': BadRequest, // An invalid symbol was specified 'InvalidTimestampInPayload': BadRequest, // The JSON payload contained a timestamp parameter with an unsupported value. 'Maintenance': OnMaintenance, // The system is down for maintenance 'MarketNotOpen': InvalidOrder, // The order was rejected because the market is not accepting new orders 'MissingApikeyHeader': AuthenticationError, // The X-GEMINI-APIKEY header was missing 'MissingOrderField': InvalidOrder, // A required order_id field was not specified 'MissingRole': AuthenticationError, // The API key used to access this endpoint does not have the required role assigned to it 'MissingPayloadHeader': AuthenticationError, // The X-GEMINI-PAYLOAD header was missing 'MissingSignatureHeader': AuthenticationError, // The X-GEMINI-SIGNATURE header was missing 'NoSSL': AuthenticationError, // You must use HTTPS to access the API 'OptionsMustBeArray': BadRequest, // The options parameter must be an array. 'OrderNotFound': OrderNotFound, // The order specified was not found 'RateLimit': RateLimitExceeded, // Requests were made too frequently. See Rate Limits below. 'System': ExchangeError, // We are experiencing technical issues 'UnsupportedOption': BadRequest, // This order execution option is not supported. }, 'broad': { 'The Gemini Exchange is currently undergoing maintenance.': OnMaintenance, // The Gemini Exchange is currently undergoing maintenance. Please check https://status.gemini.com/ for more information. }, }, 'options': { 'fetchMarketsMethod': 'fetch_markets_from_web', }, }); } async fetchMarkets (params = {}) { const method = this.safeValue (this.options, 'fetchMarketsMethod', 'fetch_markets_from_api'); return await this[method] (params); } async fetchMarketsFromWeb (symbols = undefined, params = {}) { const response = await this.webGetRestApi (params); const sections = response.split ('

Symbols and minimums

'); const numSections = sections.length; const error = this.id + ' the ' + this.name + ' API doc HTML markup has changed, breaking the parser of order limits and precision info for ' + this.name + ' markets.'; if (numSections !== 2) { throw new NotSupported (error); } const tables = sections[1].split ('tbody>'); const numTables = tables.length; if (numTables < 2) { throw new NotSupported (error); } const rows = tables[1].split ("\n\n"); // eslint-disable-line quotes const numRows = rows.length; if (numRows < 2) { throw new NotSupported (error); } const result = []; // skip the first element (empty string) for (let i = 1; i < numRows; i++) { const row = rows[i]; const cells = row.split ("\n"); // eslint-disable-line quotes const numCells = cells.length; if (numCells < 9) { throw new NotSupported (error); } // [ // 'BTC', // currency // '0.00001 BTC (1e-5)', // min order size // '0.00000001 BTC (1e-8)', // tick size // '0.01 USD', // usd price increment // 'N/A', // btc price increment // '0.0001 ETH (1e-4)', // eth price increment // '0.0001 BCH (1e-4)', // bch price increment // '0.001 LTC (1e-3)', // ltc price increment // '' // ] // const uppercaseBaseId = cells[0].replace ('', ''); const baseId = uppercaseBaseId.toLowerCase (); const base = this.safeCurrencyCode (baseId); const quoteIds = [ 'usd', 'btc', 'eth', 'bch', 'ltc' ]; const minAmountString = cells[1].replace ('', ''); const minAmountParts = minAmountString.split (' '); const minAmount = this.safeFloat (minAmountParts, 0); const amountPrecisionString = cells[2].replace ('', ''); const amountPrecisionParts = amountPrecisionString.split (' '); const amountPrecision = this.precisionFromString (amountPrecisionParts[0]); for (let j = 0; j < quoteIds.length; j++) { const quoteId = quoteIds[j]; const quote = this.safeCurrencyCode (quoteId); const pricePrecisionIndex = this.sum (3, j); const pricePrecisionString = cells[pricePrecisionIndex].replace ('', ''); if (pricePrecisionString === 'N/A') { continue; } const pricePrecisionParts = pricePrecisionString.split (' '); const pricePrecision = this.precisionFromString (pricePrecisionParts[0]); const symbol = base + '/' + quote; const id = baseId + quoteId; const active = undefined; result.push ({ 'id': id, 'info': row, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': { 'amount': amountPrecision, 'price': pricePrecision, }, 'limits': { 'amount': { 'min': minAmount, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } } return result; } async fetchMarketsFromAPI (params = {}) { const response = await this.publicGetV1Symbols (params); const result = []; for (let i = 0; i < response.length; i++) { const id = response[i]; const market = id; const baseId = id.slice (0, 3); const quoteId = id.slice (3, 6); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': undefined, 'price': undefined, }; result.push ({ 'id': id, 'info': market, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'active': undefined, }); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; if (limit !== undefined) { request['limit_bids'] = limit; request['limit_asks'] = limit; } const response = await this.publicGetV1BookSymbol (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const ticker = await this.publicGetV1PubtickerSymbol (this.extend (request, params)); const timestamp = this.safeInteger (ticker['volume'], 'timestamp'); const baseCurrency = market['base']; // unified structures are guaranteed to have unified fields const quoteCurrency = market['quote']; // so we don't need safe-methods for unified structures const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker['volume'], baseCurrency), 'quoteVolume': this.safeFloat (ticker['volume'], quoteCurrency), 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.safeInteger (trade, 'timestampms'); const id = this.safeString (trade, 'tid'); const orderId = this.safeString (trade, 'order_id'); const feeCurrencyId = this.safeString (trade, 'fee_currency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); const fee = { 'cost': this.safeFloat (trade, 'fee_amount'), 'currency': feeCurrencyCode, }; const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } const type = undefined; const side = this.safeStringLower (trade, 'type'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'order': orderId, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'cost': cost, 'amount': amount, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetV1TradesSymbol (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostV1Balances (params); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['total'] = this.safeFloat (balance, 'amount'); result[code] = account; } return this.parseBalance (result); } parseOrder (order, market = undefined) { const timestamp = this.safeInteger (order, 'timestampms'); const amount = this.safeFloat (order, 'original_amount'); const remaining = this.safeFloat (order, 'remaining_amount'); const filled = this.safeFloat (order, 'executed_amount'); let status = 'closed'; if (order['is_live']) { status = 'open'; } if (order['is_cancelled']) { status = 'canceled'; } const price = this.safeFloat (order, 'price'); const average = this.safeFloat (order, 'avg_execution_price'); let cost = undefined; if (filled !== undefined) { if (average !== undefined) { cost = filled * average; } } let type = this.safeString (order, 'type'); if (type === 'exchange limit') { type = 'limit'; } else if (type === 'market buy' || type === 'market sell') { type = 'market'; } else { type = order['type']; } const fee = undefined; let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (order, 'order_id'); const side = this.safeStringLower (order, 'side'); const clientOrderId = this.safeString (order, 'client_order_id'); return { 'id': id, 'clientOrderId': clientOrderId, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'average': average, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'fee': fee, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privatePostV1OrderStatus (this.extend (request, params)); return this.parseOrder (response); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.privatePostV1Orders (params); let orders = this.parseOrders (response, undefined, since, limit); if (symbol !== undefined) { const market = this.market (symbol); // throws on non-existent symbol orders = this.filterBySymbol (orders, market['symbol']); } return orders; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } const nonce = this.nonce (); const request = { 'client_order_id': nonce.toString (), 'symbol': this.marketId (symbol), 'amount': amount.toString (), 'price': price.toString (), 'side': side, 'type': 'exchange limit', // gemini allows limit orders only }; const response = await this.privatePostV1OrderNew (this.extend (request, params)); return { 'info': response, 'id': response['order_id'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; return await this.privatePostV1OrderCancel (this.extend (request, params)); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit_trades'] = limit; } if (since !== undefined) { request['timestamp'] = parseInt (since / 1000); } const response = await this.privatePostV1Mytrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'amount': amount, 'address': address, }; const response = await this.privatePostV1WithdrawCurrency (this.extend (request, params)); return { 'info': response, 'id': this.safeString (response, 'txHash'), }; } nonce () { return this.milliseconds (); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (limit !== undefined) { request['limit_transfers'] = limit; } if (since !== undefined) { request['timestamp'] = since; } const response = await this.privatePostV1Transfers (this.extend (request, params)); return this.parseTransactions (response); } parseTransaction (transaction, currency = undefined) { const timestamp = this.safeInteger (transaction, 'timestampms'); const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const address = this.safeString (transaction, 'destination'); const type = this.safeStringLower (transaction, 'type'); let status = 'pending'; // When deposits show as Advanced or Complete they are available for trading. if (transaction['status']) { status = 'ok'; } let fee = undefined; const feeAmount = this.safeFloat (transaction, 'feeAmount'); if (feeAmount !== undefined) { fee = { 'cost': feeAmount, 'currency': code, }; } return { 'info': transaction, 'id': this.safeString (transaction, 'eid'), 'txid': this.safeString (transaction, 'txHash'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': undefined, // or is it defined? 'type': type, // direction of the transaction, ('deposit' | 'withdraw') 'amount': this.safeFloat (transaction, 'amount'), 'currency': code, 'status': status, 'updated': undefined, 'fee': fee, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce (); const request = this.extend ({ 'request': url, 'nonce': nonce, }, query); let payload = this.json (request); payload = this.stringToBase64 (this.encode (payload)); const signature = this.hmac (payload, this.encode (this.secret), 'sha384'); headers = { 'Content-Type': 'text/plain', 'X-GEMINI-APIKEY': this.apiKey, 'X-GEMINI-PAYLOAD': this.decode (payload), 'X-GEMINI-SIGNATURE': signature, }; } else { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } url = this.urls['api'][api] + url; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { if (typeof body === 'string') { const feedback = this.id + ' ' + body; this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); } return; // fallback to default error handler } // // { // "result": "error", // "reason": "BadNonce", // "message": "Out-of-sequence nonce <1234> precedes previously used nonce <2345>" // } // const result = this.safeString (response, 'result'); if (result === 'error') { const reason = this.safeString (response, 'reason'); const message = this.safeString (response, 'message'); const feedback = this.id + ' ' + message; this.throwExactlyMatchedException (this.exceptions['exact'], reason, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privatePostV1DepositCurrencyNewAddress (this.extend (request, params)); const address = this.safeString (response, 'address'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': undefined, 'info': response, }; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'timeframe': this.timeframes[timeframe], 'symbol': market['id'], }; const response = await this.publicGetV2CandlesSymbolTimeframe (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } }; },{"./base/Exchange":7,"./base/errors":9}],89:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { AuthenticationError, ExchangeError, OrderNotFound, ArgumentsRequired, BadSymbol, BadRequest, NullResponse, InvalidOrder, BadResponse, NotSupported, ExchangeNotAvailable, RequestTimeout, RateLimitExceeded, PermissionDenied, InsufficientFunds, InvalidAddress } = require ('./base/errors'); const { TICK_SIZE, TRUNCATE } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class hbtc extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'hbtc', 'name': 'HBTC', 'countries': [ 'CN' ], 'rateLimit': 2000, 'version': 'v1', 'has': { 'CORS': false, 'fetchTime': true, 'fetchBidAsk': true, 'fetchBidsAsks': true, 'fetchTickers': true, 'fetchTicker': true, 'fetchDepositAddress': false, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrders': false, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchTradingLimits': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'withdraw': true, 'fetchCurrencies': false, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchAccounts': true, 'fetchLedger': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '8h': '8h', '12h': '12h', '1d': '1d', '3d': '3d', '1w': '1w', '1M': '1M', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/80134449-70663300-85a7-11ea-8942-e204cdeaab5d.jpg', // 交易所LOGO 'api': { 'quote': 'https://api.hbtc.com/openapi/quote', // 市场API数据端点 'contract': 'https://api.hbtc.com/openapi/contract', // 合约API数据端点 'option': 'https://api.hbtc.com/openapi/option', // 合约API数据端点 'public': 'https://api.hbtc.com/openapi', // 公共API数据端点 'private': 'https://api.hbtc.com/openapi', // 私有API数据端点 'zendesk': 'https://hbtc.zendesk.com/hc/en-us', }, 'www': 'https://www.hbtc.com', // 公司主页 'referral': 'https://www.hbtc.com/register/O2S8NS', // 邀请链接 'doc': 'https://github.com/bhexopen/BHEX-OpenApi/tree/master/doc', // openapi文档地址 'fees': 'https://hbtc.zendesk.com/hc/zh-cn/articles/360009274694', // 费率介绍 }, 'api': { 'public': { 'get': [ 'ping', 'time', 'brokerInfo', // 查询当前broker交易规则和symbol信息 'getOptions', ], }, 'quote': { 'get': [ 'depth', // 获取深度 'depth/merged', 'trades', // 获取当前最新成交 'klines', // 获取K线数据 'ticker/24hr', // 获取24小时价格变化数据 'ticker/price', 'ticker/bookTicker', 'contract/index', // 获取合约标的指数价格 'contract/depth', // 获取合约深度 'contract/depth/merged', 'contract/trades', // 获取合约最近成交, 'contract/klines', // 获取合约的K线数据 'contract/ticker/24hr', 'option/index', 'option/depth', 'option/depth/merged', 'option/trades', 'option/klines', 'option/ticker/24hr', ], }, 'contract': { 'get': [ // public 'insurance', 'fundingRate', // 获取资金费率信息 // private 'openOrders', // 查询合约当前委托 'historyOrders', // 查询合约历史委托 'getOrder', // 查询合约订单详情 'myTrades', // 查询合约历史成交 'positions', // 查询合约当前持仓 'account', // 查询合约账户信息 ], 'post': [ 'order', // 创建合约订单 'modifyMargin', // 修改保证金 ], 'delete': [ 'order/cancel', // 取消合约订单 'order/batchCancel', ], }, 'option': { 'get': [ 'openOrders', 'positions', 'historyOrders', // 'getOrder', 'myTrades', 'settlements', 'account', ], 'post': [ 'order', ], 'delete': [ 'order/cancel', ], }, 'private': { 'get': [ 'order', // 查询订单 'openOrders', // 查询当前委托 'historyOrders', // 查询历史委托 'account', // 获取当前账户信息 'myTrades', // 查询历史成交 'depositOrders', 'withdrawalOrders', 'withdraw/detail', 'balance_flow', ], 'post': [ 'order', // 创建新订单 'order/test', 'userDataStream', 'subAccount/query', 'transfer', 'user/transfer', 'withdraw', ], 'put': [ 'userDataStream', ], 'delete': [ 'order', // 取消订单 'userDataStream', ], }, }, 'precisionMode': TICK_SIZE, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.002, // MAKER费率 'taker': 0.002, // TAKER费率 }, }, 'exceptions': { 'exact': { // general server or network errors '-1000': ExchangeError, // An unknown error occured while processing the request '-1001': ExchangeError, // Internal error, unable to process your request. Please try again '-1002': AuthenticationError, // You are not authorized to execute this request. Request need API Key included in. We suggest that API Key be included in any request '-1003': RateLimitExceeded, // Too many requests, please use the websocket for live updates '-1004': BadRequest, '-1005': PermissionDenied, '-1006': BadResponse, // An unexpected response was received from the message bus. Execution status unknown. OPEN API server find some exception in execute request.Please report to Customer service '-1007': RequestTimeout, // Timeout waiting for response from backend server. Send status unknown, execution status unknown '-1014': InvalidOrder, // Unsupported order combination '-1015': RateLimitExceeded, // Reach the rate limit.Please slow down your request speed '-1016': ExchangeNotAvailable, // This service is no longer available '-1020': NotSupported, // This operation is not supported '-1021': BadRequest, // Timestamp for this request is outside of the recvWindow '-1022': AuthenticationError, // Signature for this request is not valid // request issues '-1100': BadRequest, // Illegal characters found in a parameter '-1101': BadRequest, // Too many parameters sent for this endpoint '-1102': BadRequest, // A mandatory parameter was not sent, was empty/null, or malformed '-1103': BadRequest, // An unknown parameter was sent '-1104': BadRequest, // Not all sent parameters were read '-1105': BadRequest, // A parameter was empty '-1106': BadRequest, // A parameter was sent when not required '-1111': BadRequest, // Precision is over the maximum defined for this asset '-1112': NullResponse, // No orders on book for symbol '-1114': InvalidOrder, // TimeInForce parameter sent when not required '-1115': InvalidOrder, // Invalid timeInForce '-1116': InvalidOrder, // Invalid orderType '-1117': InvalidOrder, // Invalid side '-1118': InvalidOrder, // New client order ID was empty '-1119': InvalidOrder, // Original client order ID was empty '-1120': BadRequest, // Invalid interval '-1121': BadSymbol, // Invalid symbol '-1125': AuthenticationError, // This listenKey does not exist '-1127': BadRequest, // Lookup interval is too big '-1128': BadRequest, // Combination of optional parameters invalid '-1130': BadRequest, // Invalid data sent for a parameter '-1131': InsufficientFunds, '-1132': InvalidOrder, // Order price too high '-1133': InvalidOrder, // Order price lower than the minimum,please check general broker info '-1134': InvalidOrder, // Order price decimal too long,please check general broker info '-1135': InvalidOrder, // Order quantity too large '-1136': InvalidOrder, // Order quantity lower than the minimum '-1137': InvalidOrder, // Order quantity decimal too long '-1138': InvalidOrder, // Order price exceeds permissible range '-1139': InvalidOrder, // Order has been filled '-1140': InvalidOrder, // Transaction amount lower than the minimum '-1141': InvalidOrder, // Duplicate clientOrderId '-1142': InvalidOrder, // Order has been canceled '-1143': OrderNotFound, // Cannot be found on order book '-1144': InvalidOrder, // Order has been locked '-1145': InvalidOrder, // This order type does not support cancellation '-1146': RequestTimeout, // Order creation timeout '-1147': RequestTimeout, // Order cancellation timeout '-1149': InvalidOrder, // Create order failed '-1187': InvalidAddress, // Withdrawal address not in whitelist '-2010': InvalidOrder, // NEW_ORDER_REJECTED '-2011': InvalidOrder, // CANCEL_REJECTED '-2013': OrderNotFound, // Order does not exist '-2014': AuthenticationError, // API-key format invalid '-2015': AuthenticationError, // Invalid API-key, IP, or permissions for action '-2016': ExchangeError, // No trading window could be found for the symbol. Try ticker/24hrs instead }, }, // exchange-specific options 'options': { 'fetchTickers': { 'method': 'quoteGetTicker24hr', }, }, }); } async fetchTime (params = {}) { const response = await this.publicGetTime (params); // // { // "serverTime": 1527777538000 // } // return this.safeInteger (response, 'serverTime'); } parseMarket (market, type = 'spot') { const filters = this.safeValue (market, 'filters', []); const id = this.safeString (market, 'symbol'); let baseId = this.safeString (market, 'baseAsset'); const quoteId = this.safeString (market, 'quoteAsset'); let base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); let symbol = base + '/' + quote; let spot = true; let future = false; let option = false; let inverse = false; if (type === 'future') { symbol = id; spot = false; future = true; inverse = this.safeValue (market, 'inverse', false); baseId = this.safeString (market, 'underlying'); base = this.safeCurrencyCode (baseId); } else if (type === 'option') { symbol = id; spot = false; option = true; } let amountMin = undefined; let amountMax = undefined; let priceMin = undefined; let priceMax = undefined; let costMin = undefined; for (let j = 0; j < filters.length; j++) { const filter = filters[j]; const filterType = this.safeString (filter, 'filterType'); if (filterType === 'LOT_SIZE') { amountMin = this.safeFloat (filter, 'minQty'); amountMax = this.safeFloat (filter, 'maxQty'); } if (filterType === 'PRICE_FILTER') { priceMin = this.safeFloat (filter, 'minPrice'); priceMax = this.safeFloat (filter, 'maxPrice'); } if (filterType === 'MIN_NOTIONAL') { costMin = this.safeFloat (filter, 'minNotional'); } } if ((costMin === undefined) && (amountMin !== undefined) && (priceMin !== undefined)) { costMin = amountMin * priceMin; } const precision = { 'price': this.safeFloat2 (market, 'quotePrecision', 'quoteAssetPrecision'), 'amount': this.safeFloat (market, 'baseAssetPrecision'), }; const limits = { 'amount': { 'min': amountMin, 'max': amountMax, }, 'price': { 'min': priceMin, 'max': priceMax, }, 'cost': { 'min': costMin, 'max': undefined, }, }; return { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'type': type, 'spot': spot, 'future': future, 'option': option, 'inverse': inverse, 'precision': precision, 'limits': limits, 'info': market, }; } async fetchMarkets (params = {}) { const response = await this.publicGetBrokerInfo (params); // // { // "timezone":"UTC", // "serverTime":"1588015885118", // "brokerFilters":[], // "symbols":[ // { // "filters":[ // {"minPrice":"0.01","maxPrice":"100000.00000000","tickSize":"0.01","filterType":"PRICE_FILTER"}, // {"minQty":"0.0005","maxQty":"100000.00000000","stepSize":"0.000001","filterType":"LOT_SIZE"}, // {"minNotional":"5","filterType":"MIN_NOTIONAL"} // ], // "exchangeId":"301", // "symbol":"BTCUSDT", // "symbolName":"BTCUSDT", // "status":"TRADING", // "baseAsset":"BTC", // "baseAssetPrecision":"0.000001", // "quoteAsset":"USDT", // "quotePrecision":"0.01", // "icebergAllowed":false // }, // ], // "options":[ // { // "filters":[ // {"minPrice":"0.01","maxPrice":"100000.00000000","tickSize":"0.01","filterType":"PRICE_FILTER"}, // {"minQty":"0.01","maxQty":"100000.00000000","stepSize":"0.001","filterType":"LOT_SIZE"}, // {"minNotional":"1","filterType":"MIN_NOTIONAL"} // ], // "exchangeId":"301", // "symbol":"BTC0501CS8500", // "symbolName":"BTC0501CS8500", // "status":"TRADING", // "baseAsset":"BTC0501CS8500", // "baseAssetPrecision":"0.001", // "quoteAsset":"BUSDT", // "quotePrecision":"0.01", // "icebergAllowed":false // }, // ], // "contracts":[ // { // "filters":[ // {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"}, // {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"}, // {"minNotional":"0.000001","filterType":"MIN_NOTIONAL"} // ], // "exchangeId":"301", // "symbol":"BTC-PERP-REV", // "symbolName":"BTC-PERP-REV", // "status":"TRADING", // "baseAsset":"BTC-PERP-REV", // "baseAssetPrecision":"1", // "quoteAsset":"USDT", // "quoteAssetPrecision":"0.1", // "icebergAllowed":false, // "inverse":true, // "index":"BTCUSDT", // "marginToken":"TBTC", // "marginPrecision":"0.00000001", // "contractMultiplier":"1.0", // "underlying":"TBTC", // "riskLimits":[ // {"riskLimitId":"200000001","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"}, // {"riskLimitId":"200000002","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"}, // {"riskLimitId":"200000003","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"}, // {"riskLimitId":"200000004","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"} // ] // }, // { // "filters":[ // {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"}, // {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"}, // {"minNotional":"0.000001","filterType":"MIN_NOTIONAL"} // ], // "exchangeId":"301", // "symbol":"BTC-SWAP", // "symbolName":"BTC-SWAP", // "status":"TRADING", // "baseAsset":"BTC-SWAP", // "baseAssetPrecision":"1", // "quoteAsset":"USDT", // "quoteAssetPrecision":"0.1", // "icebergAllowed":false, // "inverse":true, // "index":"BTCUSDT", // "marginToken":"BTC", // "marginPrecision":"0.00000001", // "contractMultiplier":"1.0", // "underlying":"BTC", // "riskLimits":[ // {"riskLimitId":"500000001","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"}, // {"riskLimitId":"500000002","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"}, // {"riskLimitId":"500000003","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"}, // {"riskLimitId":"500000004","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"} // ] // }, // { // "filters":[ // {"minPrice":"0.1","maxPrice":"100000.00000000","tickSize":"0.1","filterType":"PRICE_FILTER"}, // {"minQty":"1","maxQty":"100000.00000000","stepSize":"1","filterType":"LOT_SIZE"}, // {"minNotional":"0.000000001","filterType":"MIN_NOTIONAL"} // ], // "exchangeId":"301", // "symbol":"BTC-PERP-BUSDT", // "symbolName":"BTC-PERP-BUSDT", // "status":"TRADING", // "baseAsset":"BTC-PERP-BUSDT", // "baseAssetPrecision":"1", // "quoteAsset":"BUSDT", // "quoteAssetPrecision":"0.1", // "icebergAllowed":false, // "inverse":false, // "index":"BTCUSDT", // "marginToken":"BUSDT", // "marginPrecision":"0.0001", // "contractMultiplier":"0.0001", // "underlying":"TBTC", // "riskLimits":[ // {"riskLimitId":"600000132","quantity":"1000000.0","initialMargin":"0.01","maintMargin":"0.005"}, // {"riskLimitId":"600000133","quantity":"2000000.0","initialMargin":"0.02","maintMargin":"0.01"}, // {"riskLimitId":"600000134","quantity":"3000000.0","initialMargin":"0.03","maintMargin":"0.015"}, // {"riskLimitId":"600000135","quantity":"4000000.0","initialMargin":"0.04","maintMargin":"0.02"} // ] // }, // ] // } // const result = []; const symbols = this.safeValue (response, 'symbols', []); for (let i = 0; i < symbols.length; i++) { const market = this.parseMarket (symbols[i], 'spot'); result.push (market); } const options = this.safeValue (response, 'options', []); for (let i = 0; i < options.length; i++) { const market = this.parseMarket (options[i], 'option'); result.push (market); } const contracts = this.safeValue (response, 'contracts', []); for (let i = 0; i < contracts.length; i++) { const market = this.parseMarket (contracts[i], 'future'); result.push (market); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 40, max 40 } const response = await this.quoteGetDepth (this.extend (request, params)); // // { // "time":1588068913453, // "bids":[ // ["0.025278","0.0202"], // ["0.025277","16.1132"], // ["0.025276","7.9056"], // ] // "asks":[ // ["0.025302","5.9999"], // ["0.025303","34.9151"], // ["0.025304","92.391"], // ] // } // const timestamp = this.safeInteger (response, 'time'); return this.parseOrderBook (response, timestamp); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.quoteGetTicker24hr (this.extend (request, params)); // // { // "time":1588069860794, // "symbol":"BNB0501PS16", // "bestBidPrice":"0.2129", // "bestAskPrice":"0.3163", // "volume":"33547", // "quoteVolume":"10801.987", // "lastPrice":"0.2625", // "highPrice":"0.3918", // "lowPrice":"0.2625", // "openPrice":"0.362", // } // return this.parseTicker (response, market); } parseTickers (rawTickers, symbols = undefined) { const tickers = []; for (let i = 0; i < rawTickers.length; i++) { tickers.push (this.parseTicker (rawTickers[i])); } return this.filterByArray (tickers, 'symbol', symbols); } async fetchBidAsk (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.quoteGetTickerBookTicker (this.extend (request, params)); // // { // "symbol": "LTCBTC", // "bidPrice": "4.00000000", // "bidQty": "431.00000000", // "askPrice": "4.00000200", // "askQty": "9.00000000" // } // return this.parseTicker (response, market); } async fetchBidsAsks (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.quoteGetTickerBookTicker (params); // // [ // { // "symbol": "LTCBTC", // "bidPrice": "4.00000000", // "bidQty": "431.00000000", // "askPrice": "4.00000200", // "askQty": "9.00000000" // }, // { // "symbol": "ETHBTC", // "bidPrice": "0.07946700", // "bidQty": "9.00000000", // "askPrice": "100000.00000000", // "askQty": "1000.00000000" // }, // ] // return this.parseTickers (response, symbols); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const options = this.safeValue (this.options, 'fetchTickers', {}); const defaultMethod = this.safeString (options, 'method', 'quoteGetTicker24hr'); const defaultType = this.safeString (options, 'type', 'spot'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); let method = defaultMethod; if (type === 'future') { method = 'quoteGetContractTicker24hr'; } else if (type === 'option') { method = 'quoteGetOptionTicker24hr'; } const response = await this[method] (query); // // [ // { // "time": 1538725500422, // "symbol": "ETHBTC", // "lastPrice": "4.00000200", // "openPrice": "99.00000000", // "highPrice": "100.00000000", // "lowPrice": "0.10000000", // "volume": "8913.30000000" // }, // ] // return this.parseTickers (response, symbols); } async fetchBalance (params = {}) { await this.loadMarkets (); const options = this.safeValue (this.options, 'fetchBalance', {}); const defaultType = this.safeString (options, 'type', 'spot'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); let method = 'privateGetAccount'; if (type === 'future') { method = 'contractGetAccount'; } else if (type === 'option') { method = 'optionGetAccount'; } const response = await this[method] (query); // // spot // // { // 'balances': [ // { // 'asset': 'ALGO', // 'free': '0', // 'locked': '0' // }, // { // 'asset': 'BHT', // 'free': '0', // 'locked': '0' // } // ] // } // // contract // // { // "BUSDT":{ // "total":"1000", // "availableMargin":"1000", // "positionMargin":"0", // "orderMargin":"0", // "tokenId":"BUSDT" // }, // "TBTC":{ // "total":"0.5", // "availableMargin":"0.5", // "positionMargin":"0", // "orderMargin":"0", // "tokenId":"TBTC" // } // } // // option // // { // "optionAsset":"", // "balances":[ // { // "tokenName":"USDT", // "free":"0.0", // "locked":"0.0", // "margin":"0.0" // }, // { // "tokenName":"BUSDT", // "free":"0.0", // "locked":"0.0", // "margin":"0.0" // } // ] // } // const balances = this.safeValue (response, 'balances'); const result = { 'info': response }; if (balances !== undefined) { for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString2 (balance, 'asset', 'tokenName'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'free'); account['used'] = this.safeFloat (balance, 'locked'); result[code] = account; } } else { const currencyIds = Object.keys (response); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const balance = response[currencyId]; const account = this.account (); account['free'] = this.safeFloat (balance, 'availableMargin'); account['total'] = this.safeFloat (balance, 'total'); result[code] = account; } } return this.parseBalance (result); } async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 500, max 1000 } const response = await this.quoteGetTrades (this.extend (request, params)); // // [ // {"price":"0.025344","time":1588084082060,"qty":"1","isBuyerMaker":false}, // {"price":"0.02535","time":1588084086021,"qty":"0.553","isBuyerMaker":true}, // {"price":"0.025348","time":1588084097037,"qty":"1","isBuyerMaker":false}, // ] // return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // [ // 1587906000000, // open time // "0.1761", // open // "0.1761", // high // "0.1761", // low // "0.1761", // close // "0", // base volume // 0, // close time // "0", // quote volume // 0, // number of trades // "0", // taker buy base asset volume // "0" // taker buy quote asset volume // ] // return [ this.safeInteger (ohlcv, 0), this.safeFloat (ohlcv, 1), this.safeFloat (ohlcv, 2), this.safeFloat (ohlcv, 3), this.safeFloat (ohlcv, 4), this.safeFloat (ohlcv, 5), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'interval': this.timeframes[timeframe], }; if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['limit'] = limit; // default 500, max 500 } const response = await this.quoteGetKlines (this.extend (request, params)); // // [ // [1587906000000,"0.1761","0.1761","0.1761","0.1761","0",0,"0",0,"0","0"], // [1587906180000,"0.1761","0.1761","0.1761","0.1761","0",0,"0",0,"0","0"], // [1587906360000,"0.1761","0.1848","0.1761","0.1848","53",0,"9.7944",1,"0","0"], // ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = { // if only fromId is set,it will get orders < that fromId in descending order // if only toId is set, it will get orders > that toId in ascending order // if fromId is set and toId is set, it will get orders < that fromId and > that toId in descending order // if fromId is not set and toId it not set, most recent order are returned in descending order // 'fromId': '43287482374', // 'toId': '43287482374', // 'endTime': this.milliseconds (), // optional, spot only }; const defaultType = this.safeString (this.options, 'type', 'spot'); const options = this.safeValue (this.options, 'fetchMyTrades', {}); const fetchMyTradesType = this.safeString (options, 'type', defaultType); let type = this.safeString (params, 'type', fetchMyTradesType); if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; type = market['type']; } const query = this.omit (params, 'type'); if (limit !== undefined) { // spot default 500, max 1000 // futures and options default 20, max 1000 request['limit'] = limit; } let method = 'privateGetMyTrades'; if (type === 'future') { method = 'contractGetMyTrades'; } else { if (type === 'option') { method = 'optionGetMyTrades'; } else { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument for ' + type + ' markets'); } const market = this.market (symbol); request['symbol'] = market['id']; // spot only? if (since !== undefined) { request['startTime'] = since; } } } if (since !== undefined) { request['startTime'] = since; } const response = await this[method] (this.extend (request, query)); // // spot // // [ // { // "id":"616384027512920576", // "symbol":"TBTCBUSDT", // "orderId":"616384027202542080", // "matchOrderId":"605124954767266560", // "price":"6826.06", // "qty":"0.1", // "commission":"0.682606", // "commissionAsset":"BUSDT", // "time":"1588214701982", // "isBuyer":false, // "isMaker":false, // "fee":{ // "feeTokenId":"BUSDT", // "feeTokenName":"BUSDT", // "fee":"0.682606" // } // } // ] // return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const orderSide = side.toUpperCase (); const orderType = type.toUpperCase (); const request = { 'symbol': market['id'], // BUY or SELL for spot and options 'side': orderSide, // GTC, FOK, IOC for spot and options // GTC, FOK, IOC, LIMIT_MAKER for futures // 'timeInForce': 'GTC', }; let query = params; let method = 'privatePostOrder'; if (market['type'] === 'future') { if ((orderSide !== 'BUY_OPEN') && (orderSide !== 'SELL_OPEN') && (orderSide !== 'BUY_CLOSE') && (orderSide !== 'SELL_CLOSE')) { throw new NotSupported (this.id + ' createOrder() does not support order side ' + side + ' for ' + market['type'] + ' markets, only BUY_OPEN, SELL_OPEN, BUY_CLOSE and SELL_CLOSE are supported'); } if ((orderType !== 'LIMIT') && (orderType !== 'STOP')) { throw new NotSupported (this.id + ' createOrder() does not support order type ' + type + ' for ' + market['type'] + ' markets, only LIMIT and STOP are supported'); } const clientOrderId = this.safeValue (params, 'clientOrderId'); if (clientOrderId === undefined) { throw new ArgumentsRequired (this.id + ' createOrder() requires a clientOrderId parameter for ' + market['type'] + ' markets, supply clientOrderId in the params argument'); } const leverage = this.safeValue (params, 'leverage'); if (leverage === undefined && (orderSide === 'BUY_OPEN' || orderSide === 'SELL_OPEN')) { throw new NotSupported (this.id + ' createOrder() requires a leverage parameter for ' + market['type'] + ' markets if orderSide is BUY_OPEN or SELL_OPEN'); } method = 'contractPostOrder'; const priceType = this.safeString (params, 'priceType'); if (priceType === undefined) { request['price'] = this.priceToPrecision (symbol, price); } else { request['priceType'] = priceType; if (priceType === 'INPUT') { request['price'] = this.priceToPrecision (symbol, price); } } request['orderType'] = type.toUpperCase (); // LIMIT, STOP request['quantity'] = this.amountToPrecision (symbol, amount); // request['leverage'] = 1; // not required for closing orders request['leverage'] = leverage; request['clientOrderId'] = clientOrderId; // optional // request['priceType'] = 'INPUT', // INPUT, OPPONENT, QUEUE, OVER, MARKET // request['triggerPrice'] = 123.45; } else { if (market['type'] === 'option') { method = 'optionPostOrder'; } const newClientOrderId = this.safeValue2 (params, 'clientOrderId', 'newClientOrderId'); if (newClientOrderId !== undefined) { request['newClientOrderId'] = newClientOrderId; } request['type'] = orderType; if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); request['quantity'] = this.amountToPrecision (symbol, amount); } else if (type === 'market') { // for market buy it requires the amount of quote currency to spend if (side === 'buy') { const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true); if (createMarketBuyOrderRequiresPrice) { if (price !== undefined) { amount = amount * price; } else { throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'amount' argument (the exchange-specific behaviour)"); } } const precision = market['precision']['price']; request['quantity'] = this.decimalToPrecision (amount, TRUNCATE, precision, this.precisionMode); } else { request['quantity'] = this.amountToPrecision (symbol, amount); } } } query = this.omit (query, [ 'clientOrderId', 'newClientOrderId' ]); const response = await this[method] (this.extend (request, query)); // // spot // // { // "symbol":"TBTCBUSDT", // "orderId":"616376654496877056", // "clientOrderId":"158821382304516955", // "transactTime":"1588213823080", // "price":"0", // "origQty":"1000", // "executedQty":"0", // "status":"NEW", // "timeInForce":"GTC", // "type":"MARKET", // "side":"BUY" // } // // contract // // { // 'time': '1570759718825', // 'updateTime': '0', // 'orderId': '469961015902208000', // 'clientOrderId': '6423344174', // 'symbol': 'BTC-PERP-REV', // 'price': '8200', // 'leverage': '12.08', // 'origQty': '5', // 'executedQty': '0', // 'avgPrice': '0', // 'marginLocked': '0.00005047', // 'orderType': 'LIMIT', // 'side': 'BUY_OPEN', // 'fees': [], // 'timeInForce': 'GTC', // 'status': 'NEW', // 'priceType': 'INPUT' // } // return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const clientOrderId = this.safeValue2 (params, 'origClientOrderId', 'clientOrderId'); const request = {}; const defaultType = this.safeString (this.options, 'type', 'spot'); const options = this.safeValue (this.options, 'cancelOrder', {}); const cancelOrderType = this.safeString (options, 'type', defaultType); let type = this.safeString (params, 'type', cancelOrderType); let query = this.omit (params, 'type'); if (clientOrderId !== undefined) { request['origClientOrderId'] = clientOrderId; query = this.omit (query, [ 'origClientOrderId', 'clientOrderId' ]); } else { request['orderId'] = id; } let method = 'privateDeleteOrder'; const orderType = this.safeString (query, 'orderType'); if (orderType !== undefined) { type = 'future'; } if (type === 'future') { method = 'contractDeleteOrderCancel'; if (orderType === undefined) { throw new ArgumentsRequired (this.id + " cancelOrder() requires an orderType parameter, pass the { 'orderType': 'LIMIT' } or { 'orderType': 'STOP' } in params argument"); } request['orderType'] = orderType; } else { if (type === 'option') { method = 'optionDeleteOrderCancel'; } } const response = await this[method] (this.extend (request, query)); // // spot // // { // 'exchangeId': '301', // 'symbol': 'BHTUSDT', // 'clientOrderId': '0', // 'orderId': '499890200602846976', // 'status': 'CANCELED' // } // // futures // // { // "time":"1588353669383", // "updateTime":"0", // "orderId":"617549770304599296", // "clientOrderId":"test-001", // "symbol":"BTC-PERP-REV", // "price":"10000", // "leverage":"1", // "origQty":"100", // "executedQty":"0", // "avgPrice":"0", // "marginLocked":"0", // "orderType":"LIMIT", // "side":"SELL_OPEN", // "fees":[], // "timeInForce":"GTC", // "status":"CANCELED", // "priceType":"INPUT", // } // return this.parseOrder (response); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = { // if orderId is set, it will get orders < that orderId otherwise most recent orders are returned // 'orderId': '43287482374', }; const defaultType = this.safeString (this.options, 'type', 'spot'); const options = this.safeValue (this.options, 'fetchOpenOrders', {}); const fetchOpenOrdersType = this.safeString (options, 'type', defaultType); let type = this.safeString (params, 'type', fetchOpenOrdersType); if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; type = market['type']; } const query = this.omit (params, 'type'); if (limit !== undefined) { request['limit'] = limit; // default 500, max 1000 } let method = 'privateGetOpenOrders'; if (type === 'future') { method = 'contractGetOpenOrders'; } else if (type === 'option') { method = 'optionGetOpenOrders'; } const response = await this[method] (this.extend (request, query)); // // spot // // [ // { // 'orderId': '499902955766523648', // 'clientOrderId': '157432907618453', // 'exchangeId': '301', // 'symbol': 'BHTUSDT', // 'price': '0.01', // 'origQty': '50', // 'executedQty': '0', // 'cummulativeQuoteQty': '0', // 'avgPrice': '0', // 'status': 'NEW', // 'timeInForce': 'GTC', // 'type': 'LIMIT', // 'side': 'BUY', // 'stopPrice': '0.0', // 'icebergQty': '0.0', // 'time': '1574329076202', // 'updateTime': '0', // 'isWorking': true // } // ] // // futures // // [ // { // "time":"1588353669383", // "updateTime":"0", // "orderId":"617549770304599296", // "clientOrderId":"test-001", // "symbol":"BTC-PERP-REV", // "price":"10000", // "leverage":"1", // "origQty":"100", // "executedQty":"0", // "avgPrice":"0", // "marginLocked":"0.01", // "orderType":"LIMIT", // "side":"SELL_OPEN", // "fees":[], // "timeInForce":"GTC", // "status":"NEW", // "priceType":"INPUT" // } // ] // return this.parseOrders (response, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = { // if orderId is set, it will get orders < that orderId otherwise most recent orders are returned // 'orderId': '43287482374', // 'endTime': this.milliseconds (), // optional }; const defaultType = this.safeString (this.options, 'type', 'spot'); const options = this.safeValue (this.options, 'fetchClosedOrders', {}); const fetchClosedOrdersType = this.safeString (options, 'type', defaultType); let type = this.safeString (params, 'type', fetchClosedOrdersType); if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; type = market['type']; } const query = this.omit (params, 'type'); if (limit !== undefined) { request['limit'] = limit; // default 500, max 1000 } if (since !== undefined) { request['startTime'] = since; } let method = 'privateGetHistoryOrders'; if (type === 'future') { method = 'contractGetHistoryOrders'; } else if (type === 'option') { method = 'optionGetHistoryOrders'; } const response = await this[method] (this.extend (request, query)); // // spot // // [ // { // "orderId":"616384027202542080", // "clientOrderId":"158821470194414688", // "exchangeId":"301", // "symbol":"TBTCBUSDT", // "price":"0", // "origQty":"0.1", // "executedQty":"0.1", // "cummulativeQuoteQty":"682.606", // "avgPrice":"6826.06", // "status":"FILLED", // "timeInForce":"GTC", // "type":"MARKET", // "side":"SELL", // "stopPrice":"0.0", // "icebergQty":"0.0", // "time":"1588214701974", // "updateTime":"0", // "isWorking":true // } // ] // return this.parseOrders (response, market, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const clientOrderId = this.safeValue2 (params, 'origClientOrderId', 'clientOrderId'); const request = {}; const defaultType = this.safeString (this.options, 'type', 'spot'); const options = this.safeValue (this.options, 'fetchOrder', {}); const fetchOrderType = this.safeString (options, 'type', defaultType); const type = this.safeString (params, 'type', fetchOrderType); let query = this.omit (params, 'type'); if (clientOrderId !== undefined) { request['origClientOrderId'] = clientOrderId; query = this.omit (query, [ 'origClientOrderId', 'clientOrderId' ]); } else { request['orderId'] = id; } let method = 'privateGetOrder'; if (type === 'future') { method = 'contractGetGetOrder'; } else if (type === 'option') { method = 'optionGetGetOrder'; } const response = await this[method] (this.extend (request, query)); return this.parseOrder (response); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = { // 'fromId': 'string', // if fromId is set, it will get deposits > that fromId, otherwise most recent deposits are returned }; if (code !== undefined) { currency = this.currency (code); } if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetDepositOrders (this.extend (request, params)); // // [ // { // 'time': '1565769575929', // 'orderId': '428100569859739648', // 'token': 'USDT', // 'address': '', // 'addressTag': '', // 'fromAddress': '', // 'fromAddressTag': '', // 'quantity': '1100', // }, // ] // return this.parseTransactions (response, currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = { // 'fromId': 'string', // if fromId is set, it will get deposits > that fromId, otherwise most recent deposits are returned }; if (code !== undefined) { currency = this.currency (code); request['token'] = currency['id']; } if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['limit'] = limit; // default 500, max 1000 } const response = await this.privateGetWithdrawalOrders (this.extend (request, params)); // // [ // { // "time":"1536232111669", // "orderId":"90161227158286336", // "accountId":"517256161325920", // "tokenId":"BHC", // "tokenName":"BHC", // "address":"0x815bF1c3cc0f49b8FC66B21A7e48fCb476051209", // "addressExt":"address tag", // "quantity":"14", // Withdrawal qty // "arriveQuantity":"14", // Arrived qty // "statusCode":"PROCESSING_STATUS", // "status":3, // "txid":"", // "txidUrl":"", // "walletHandleTime":"1536232111669", // "feeTokenId":"BHC", // "feeTokenName":"BHC", // "fee":"0.1", // "requiredConfirmNum":0, // Required confirmations // "confirmNum":0, // Confirmations // "kernelId":"", // BEAM and GRIN only // "isInternalTransfer": false // True if this transfer is internal // } // ] // return this.parseTransactions (response, currency, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const clientOrderId = this.safeString (params, 'clientOrderId', this.uuid ()); const request = { 'clientOrderId': clientOrderId, 'tokenId': currency['id'], 'address': address, // the withdrawal address must be in current tag list in your PC/APP client 'withdrawQuantity': amount, // 'chainType': 'OMNI', // OMNI, ERC20, TRC20 }; if (tag !== undefined) { request['addressExt'] = tag; } const response = await this.privatePostWithdraw (this.extend (request, params)); // // { // "status": 0, // "success": true, // "needBrokerAudit": false, // Whether this request needs broker auit // "orderId": "423885103582776064" // Id for successful withdrawal // } // return { 'info': response, 'id': this.safeString (response, 'orderId'), }; } async fetchAccounts (params = {}) { const response = await this.privatePostSubAccountQuery (params); // // [ // { // "accountId": "122216245228131", // "accountName": "createSubAccountByCurl", // sub-account name // "accountType": 1, // 1 token trading, 2 options, 3 futures // "accountIndex": 1, // 0 main account, 1 sub-account // }, // ] // const result = []; for (let i = 0; i < response.length; i++) { const account = response[i]; const accountId = this.safeString (account, 'accountId'); const accountType = this.safeString (account, 'accountType'); let type = accountType; if (accountType === '1') { type = 'spot'; } else if (accountType === '2') { type = 'option'; } else if (accountType === '3') { type = 'future'; } result.push ({ 'id': accountId, 'type': type, 'currency': undefined, 'info': account, }); } return result; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'accountType': 1, // spot 1, options 2, futures 3 'accountIndex': 0, // main 0, sub-account 1 'fromFlowId': '', // flowId to start from 'endFlowId': '', // flowId to end with 'endTime': 1588450533040, }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['tokenId'] = currency['id']; } if (since !== undefined) { request['startTime'] = since; } if (limit !== undefined) { request['limit'] = limit; // default 500, max 500 } const response = await this.privateGetBalanceFlow (this.extend (request, params)); // // [ // { // "id": "539870570957903104", // "accountId": "122216245228131", // "tokenId": "BTC", // "tokenName": "BTC", // "flowTypeValue": 51, // "flowType": "USER_ACCOUNT_TRANSFER", // "flowName": "Transfer", // "change": "-12.5", // "total": "379.624059937852365", // after change // "created": "1579093587214" // }, // { // "id": "536072393645448960", // "accountId": "122216245228131", // "tokenId": "USDT", // "tokenName": "USDT", // "flowTypeValue": 7, // "flowType": "AIRDROP", // "flowName": "Airdrop", // "change": "-2000", // "total": "918662.0917630848", // "created": "1578640809195" // } // ] // return this.parseLedger (response, currency, since, limit); } parseLedgerEntry (item, currency = undefined) { // // { // "id": "539870570957903104", // "accountId": "122216245228131", // "tokenId": "BTC", // "tokenName": "BTC", // "flowTypeValue": 51, // "flowType": "USER_ACCOUNT_TRANSFER", // "flowName": "Transfer", // "change": "-12.5", // "total": "379.624059937852365", // after change // "created": "1579093587214" // } // // { // "id": "536072393645448960", // "accountId": "122216245228131", // "tokenId": "USDT", // "tokenName": "USDT", // "flowTypeValue": 7, // "flowType": "AIRDROP", // "flowName": "Airdrop", // "change": "-2000", // "total": "918662.0917630848", // "created": "1578640809195" // } // const currencyId = this.safeString (item, 'tokenId'); const code = this.safeCurrencyCode (currencyId, currency); const amount = this.safeFloat (item, 'change'); const after = this.safeFloat (item, 'total'); const direction = (amount < 0) ? 'out' : 'in'; let before = undefined; if (after !== undefined && amount !== undefined) { const difference = (direction === 'out') ? amount : -amount; before = this.sum (after, difference); } const timestamp = this.safeInteger (item, 'created'); const type = this.parseLedgerEntryType (this.safeString (item, 'flowType')); const id = this.safeString (item, 'id'); const account = this.safeString (item, 'accountId'); return { 'id': id, 'currency': code, 'account': account, 'referenceAccount': undefined, 'referenceId': undefined, 'status': undefined, 'amount': amount, 'before': before, 'after': after, 'fee': undefined, 'direction': direction, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'type': type, 'info': item, }; } parseLedgerEntryType (type) { const types = { 'TRADE': 'trade', 'FEE': 'fee', 'TRANSFER': 'transfer', 'DEPOSIT': 'transaction', 'MAKER_REWARD': 'rebate', 'PNL': 'pnl', 'SETTLEMENT': 'settlement', 'LIQUIDATION': 'liquidation', 'FUNDING_SETTLEMENT': 'settlement', 'USER_ACCOUNT_TRANSFER': 'transfer', 'OTC_BUY_COIN': 'trade', 'OTC_SELL_COIN': 'trade', 'OTC_FEE': 'fee', 'OTC_TRADE': 'trade', 'ACTIVITY_AWARD': 'referral', 'INVITATION_REFERRAL_BONUS': 'referral', 'REGISTER_BONUS': 'referral', 'AIRDROP': 'airdrop', 'MINE_REWARD': 'reward', }; return this.safeString (types, type, type); } parseTransactionStatus (status) { const statuses = { 'BROKER_AUDITING_STATUS': 'pending', 'BROKER_REJECT_STATUS': 'failed', 'AUDITING_STATUS': 'pending', 'AUDIT_REJECT_STATUS': 'failed', 'PROCESSING_STATUS': 'pending', 'WITHDRAWAL_SUCCESS_STATUS': 'ok', 'WITHDRAWAL_FAILURE_STATUS': 'failed', 'BLOCK_MINING_STATUS': 'ok', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // 'time': '1565769575929', // 'orderId': '428100569859739648', // 'token': 'USDT', // 'address': '', // 'addressTag': '', // 'fromAddress': '', // 'fromAddressTag': '', // 'quantity': '1100', // } // // fetchWithdrawals // // { // "time":"1536232111669", // "orderId":"90161227158286336", // "accountId":"517256161325920", // "tokenId":"BHC", // "tokenName":"BHC", // "address":"0x815bF1c3cc0f49b8FC66B21A7e48fCb476051209", // "addressExt":"address tag", // "quantity":"14", // Withdrawal qty // "arriveQuantity":"14", // Arrived qty // "statusCode":"PROCESSING_STATUS", // "status":3, // "txid":"", // "txidUrl":"", // "walletHandleTime":"1536232111669", // "feeTokenId":"BHC", // "feeTokenName":"BHC", // "fee":"0.1", // "requiredConfirmNum":0, // Required confirmations // "confirmNum":0, // Confirmations // "kernelId":"", // BEAM and GRIN only // "isInternalTransfer": false // True if this transfer is internal // } // const id = this.safeString (transaction, 'orderId'); const address = this.safeString (transaction, 'address'); let tag = this.safeString2 (transaction, 'addressExt', 'addressTag'); if (tag !== undefined) { if (tag.length < 1) { tag = undefined; } } const addressFrom = this.safeString (transaction, 'fromAddress'); let tagFrom = this.safeString (transaction, 'fromAddressTag'); if (tagFrom !== undefined) { if (tagFrom.length < 1) { tagFrom = undefined; } } const currencyId = this.safeString (transaction, 'tokenId'); const code = this.safeCurrencyCode (currencyId, currency); const timestamp = this.safeInteger (transaction, 'time'); let txid = this.safeString (transaction, 'txid'); if (txid === '') { txid = undefined; } let type = undefined; let status = this.parseTransactionStatus (this.safeString (transaction, 'statusCode')); if (status === undefined) { type = 'deposit'; status = 'ok'; } else { type = 'withdrawal'; } const amount = this.safeFloat (transaction, 'quantity'); const feeCost = this.safeFloat (transaction, 'fee'); let fee = undefined; if (feeCost !== undefined) { const feeCurrencyId = this.safeString (transaction, 'feeTokenId'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'currency': feeCurrencyCode, 'cost': feeCost, }; } return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'addressFrom': addressFrom, 'address': address, 'addressTo': address, 'tagFrom': tagFrom, 'tag': tag, 'tagTo': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': undefined, 'fee': fee, }; } parseTicker (ticker, market = undefined) { // // fetchTicker, fetchTickers // // { // "time":1588069860794, // "symbol":"BNB0501PS16", // "bestBidPrice":"0.2129", // "bestAskPrice":"0.3163", // "volume":"33547", // "quoteVolume":"10801.987", // "lastPrice":"0.2625", // "highPrice":"0.3918", // "lowPrice":"0.2625", // "openPrice":"0.362", // } // // fetchBidAsk, fetchBidAsks // // { // "symbol": "LTCBTC", // "bidPrice": "4.00000000", // "bidQty": "431.00000000", // "askPrice": "4.00000200", // "askQty": "9.00000000" // } // let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger (ticker, 'time'); const open = this.safeFloat (ticker, 'openPrice'); const close = this.safeFloat (ticker, 'lastPrice'); let change = undefined; let percentage = undefined; let average = undefined; if ((open !== undefined) && (close !== undefined)) { change = close - open; average = this.sum (open, close) / 2; if ((close !== undefined) && (close > 0)) { percentage = (change / open) * 100; } } const quoteVolume = this.safeFloat (ticker, 'quoteVolume'); const baseVolume = this.safeFloat (ticker, 'volume'); let vwap = undefined; if (baseVolume !== undefined && quoteVolume !== undefined && baseVolume > 0) { vwap = quoteVolume / baseVolume; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'highPrice'), 'low': this.safeFloat (ticker, 'lowPrice'), 'bid': this.safeFloat2 (ticker, 'bestBidPrice', 'bidPrice'), 'bidVolume': this.safeFloat (ticker, 'bidQty'), 'ask': this.safeFloat2 (ticker, 'bestAskPrice', 'askPrice'), 'askVolume': this.safeFloat (ticker, 'askQty'), 'vwap': vwap, 'open': open, 'close': close, 'last': close, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } parseTrade (trade, market) { // // fetchTrades (public) // // { // "price":"0.025344", // "time":1588084082060, // "qty":"1", // "isBuyerMaker":false // } // // fetchMyTrades (private) // // spot // // { // "id":"616384027512920576", // "symbol":"TBTCBUSDT", // "orderId":"616384027202542080", // "matchOrderId":"605124954767266560", // "price":"6826.06", // "qty":"0.1", // "commission":"0.682606", // "commissionAsset":"BUSDT", // "time":"1588214701982", // "isBuyer":false, // "isMaker":false, // "fee":{ // "feeTokenId":"BUSDT", // "feeTokenName":"BUSDT", // "fee":"0.682606" // } // } // const id = this.safeString (trade, 'id'); const timestamp = this.safeFloat (trade, 'time'); const type = undefined; const orderId = this.safeString (trade, 'orderId'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'qty'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let side = undefined; let takerOrMaker = undefined; if ('isBuyerMaker' in trade) { side = trade['isBuyerMaker'] ? 'sell' : 'buy'; } else { const isMaker = this.safeValue (trade, 'isMaker'); if (isMaker !== undefined) { takerOrMaker = isMaker ? 'maker' : 'taker'; } const isBuyer = this.safeValue (trade, 'isBuyer'); side = isBuyer ? 'buy' : 'sell'; } let fee = undefined; const feeCost = this.safeFloat (trade, 'commission'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'commissionAsset'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } let symbol = undefined; if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'order': orderId, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } parseOrder (order, market = undefined) { // // createOrder // // { // "symbol":"TBTCBUSDT", // "orderId":"616376654496877056", // "clientOrderId":"158821382304516955", // "transactTime":"1588213823080", // "price":"0", // "origQty":"1000", // "executedQty":"0", // "status":"NEW", // "timeInForce":"GTC", // "type":"MARKET", // "side":"BUY" // } // // fetchOrder, fetchOpenOrders, fetchClosedOrders // // spot // // { // "orderId":"616384027202542080", // "clientOrderId":"158821470194414688", // "exchangeId":"301", // "symbol":"TBTCBUSDT", // "price":"0", // "origQty":"0.1", // "executedQty":"0.1", // "cummulativeQuoteQty":"682.606", // "avgPrice":"6826.06", // "status":"FILLED", // "timeInForce":"GTC", // "type":"MARKET", // "side":"SELL", // "stopPrice":"0.0", // "icebergQty":"0.0", // "time":"1588214701974", // "updateTime":"0", // "isWorking":true // } // // future // // { // time: "1588353669383", // updateTime: "0", // orderId: "617549770304599296", // clientOrderId: "test-001", // symbol: "BTC-PERP-REV", // price: "10000", // leverage: "1", // origQty: "100", // executedQty: "0", // avgPrice: "0", // marginLocked: "0", // orderType: "LIMIT", // side: "SELL_OPEN", // fees: [], // timeInForce: "GTC", // status: "CANCELED", // priceType: "INPUT" // } // // const id = this.safeString (order, 'orderId'); const clientOrderId = this.safeString (order, 'clientOrderId'); let timestamp = this.safeInteger (order, 'time'); if (timestamp === undefined) { timestamp = this.safeInteger (order, 'transactTime'); } let symbol = undefined; if (market === undefined) { let marketId = this.safeString (order, 'symbol'); if (marketId !== undefined) { marketId = marketId.toUpperCase (); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } } let type = this.safeStringLower (order, 'type'); const side = this.safeStringLower (order, 'side'); let price = this.safeFloat (order, 'price'); let average = this.safeFloat (order, 'avgPrice'); let amount = undefined; let cost = this.safeFloat (order, 'cummulativeQuoteQty'); let filled = undefined; let remaining = undefined; if (type === undefined) { type = this.safeStringLower (order, 'orderType'); if ((market !== undefined) && market['inverse']) { cost = this.safeFloat (order, 'executedQty'); amount = undefined; } if (cost === 0.0) { filled = 0; } } else { amount = this.safeFloat (order, 'origQty'); if (type === 'market') { price = undefined; if (side === 'buy') { amount = undefined; } } filled = this.safeFloat (order, 'executedQty'); if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; } } } if (average === 0.0) { average = undefined; } const status = this.parseOrderStatus (this.safeString (order, 'status')); if (market !== undefined) { symbol = market['symbol']; } const result = { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'average': average, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'trades': undefined, 'fee': undefined, 'fees': undefined, }; const fees = this.safeValue (order, 'fees', []); const numFees = fees.length; if (numFees > 0) { result['fees'] = []; for (let i = 0; i < fees.length; i++) { const feeCost = this.safeFloat (fees[i], 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (fees[i], 'feeToken'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); result['fees'].push ({ 'cost': feeCost, 'currency': feeCurrencyCode, }); } } } return result; } parseOrderStatus (status) { const statuses = { 'NEW': 'open', 'CANCELED': 'canceled', 'FILLED': 'closed', 'PENDING_CANCEL': 'canceled', }; return this.safeString (statuses, status, status); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); const isPublicContract = (api === 'contract') && ((path === 'insurance') || (path === 'fundingRate')); if ((api === 'public') || (api === 'quote') || isPublicContract) { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { const timestamp = this.milliseconds (); this.checkRequiredCredentials (); const request = this.extend ({ 'timestamp': timestamp, }, query); // 准备待签名数据 const auth = this.urlencode (request); const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256'); request['signature'] = signature; headers = { 'X-BH-APIKEY': this.apiKey, }; if (method === 'POST') { body = this.urlencode (request); headers = this.extend ({ 'Content-Type': 'application/x-www-form-urlencoded', }, headers); } else { url += '?' + this.urlencode (request); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('code' in response) { const code = this.safeString (response, 'code'); if (code !== '0') { const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],90:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadSymbol, PermissionDenied, ExchangeError, ExchangeNotAvailable, OrderNotFound, InsufficientFunds, InvalidOrder, RequestTimeout, AuthenticationError } = require ('./base/errors'); const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class hitbtc extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'hitbtc', 'name': 'HitBTC', 'countries': [ 'HK' ], 'rateLimit': 1500, 'version': '2', 'pro': true, 'has': { 'createDepositAddress': true, 'fetchDepositAddress': true, 'CORS': false, 'editOrder': true, 'fetchCurrencies': true, 'fetchOHLCV': true, 'fetchTickers': true, 'fetchOrder': true, 'fetchOrders': false, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'withdraw': true, 'fetchOrderTrades': false, // not implemented yet 'fetchDeposits': false, 'fetchWithdrawals': false, 'fetchTransactions': true, 'fetchTradingFee': true, }, 'timeframes': { '1m': 'M1', '3m': 'M3', '5m': 'M5', '15m': 'M15', '30m': 'M30', // default '1h': 'H1', '4h': 'H4', '1d': 'D1', '1w': 'D7', '1M': '1M', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg', 'api': { 'public': 'https://api.hitbtc.com', 'private': 'https://api.hitbtc.com', }, 'www': 'https://hitbtc.com', 'referral': 'https://hitbtc.com/?ref_id=5a5d39a65d466', 'doc': [ 'https://api.hitbtc.com', 'https://github.com/hitbtc-com/hitbtc-api/blob/master/APIv2.md', ], 'fees': [ 'https://hitbtc.com/fees-and-limits', 'https://support.hitbtc.com/hc/en-us/articles/115005148605-Fees-and-limits', ], }, 'api': { 'public': { 'get': [ 'symbol', // Available Currency Symbols 'symbol/{symbol}', // Get symbol info 'currency', // Available Currencies 'currency/{currency}', // Get currency info 'ticker', // Ticker list for all symbols 'ticker/{symbol}', // Ticker for symbol 'trades/{symbol}', // Trades 'orderbook/{symbol}', // Orderbook 'candles/{symbol}', // Candles ], }, 'private': { 'get': [ 'order', // List your current open orders 'order/{clientOrderId}', // Get a single order by clientOrderId 'trading/balance', // Get trading balance 'trading/fee/all', // Get trading fee rate 'trading/fee/{symbol}', // Get trading fee rate 'history/trades', // Get historical trades 'history/order', // Get historical orders 'history/order/{id}/trades', // Get historical trades by specified order 'account/balance', // Get main acccount balance 'account/transactions', // Get account transactions 'account/transactions/{id}', // Get account transaction by id 'account/crypto/address/{currency}', // Get deposit crypro address ], 'post': [ 'order', // Create new order 'account/crypto/withdraw', // Withdraw crypro 'account/crypto/address/{currency}', // Create new deposit crypro address 'account/transfer', // Transfer amount to trading ], 'put': [ 'order/{clientOrderId}', // Create new order 'account/crypto/withdraw/{id}', // Commit withdraw crypro ], 'delete': [ 'order', // Cancel all open orders 'order/{clientOrderId}', // Cancel order 'account/crypto/withdraw/{id}', // Rollback withdraw crypro ], 'patch': [ 'order/{clientOrderId}', // Cancel Replace order ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.1 / 100, 'taker': 0.2 / 100, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BTC': 0.001, 'BCC': 0.0018, 'ETH': 0.00958, 'BCH': 0.0018, 'USDT': 100, 'DASH': 0.03, 'BTG': 0.0005, 'XRP': 0.509, 'LTC': 0.003, 'ZEC': 0.0001, 'XMR': 0.09, '1ST': 0.84, 'ADX': 5.7, 'AE': 6.7, 'AEON': 0.01006, 'AIR': 565, 'AMM': 14, 'AMP': 342, 'ANT': 6.7, 'ARDR': 1, 'ARN': 18.5, 'ART': 26, 'ATB': 0.0004, 'ATL': 27, 'ATM': 504, 'ATS': 860, 'AVT': 1.9, 'BAS': 113, 'BCN': 0.1, 'BET': 124, 'BKB': 46, 'BMC': 32, 'BMT': 100, 'BNT': 2.57, 'BQX': 4.7, 'BTCA': 351.21, 'BTM': 40, 'BTX': 0.04, 'BUS': 0.004, 'CAPP': 97, 'CCT': 6, 'CDT': 100, 'CDX': 30, 'CFI': 61, 'CL': 13.85, 'CLD': 0.88, 'CND': 574, 'CNX': 0.04, 'COSS': 65, 'CPAY': 5.487, 'CSNO': 16, 'CTR': 15, 'CTX': 146, 'CVC': 8.46, 'DATA': 12.949, 'DBIX': 0.0168, 'DCN': 1280, 'DCT': 0.02, 'DDF': 342, 'DENT': 1000, 'DGB': 0.4, 'DGD': 0.01, 'DICE': 0.32, 'DLT': 0.26, 'DNT': 0.21, 'DOGE': 2, 'DOV': 34, 'DRPU': 24, 'DRT': 240, 'DSH': 0.017, 'EBET': 84, 'EBTC': 20, 'EBTCOLD': 6.6, 'ECAT': 14, 'EDG': 2, 'EDO': 2.9, 'EKO': 1136.36, 'ELE': 0.00172, 'ELM': 0.004, 'EMC': 0.03, 'MGO': 14, 'ENJ': 163, 'EOS': 1.5, 'ERO': 34, 'ETBS': 15, 'ETC': 0.002, 'ETP': 0.004, 'EVX': 5.4, 'EXN': 456, 'FCN': 0.000005, 'FRD': 65, 'FUEL': 123.00105, 'FUN': 202.9598309, 'FYN': 1.849, 'FYP': 66.13, 'GAME': 0.004, 'GNO': 0.0034, 'GUP': 4, 'GVT': 1.2, 'HSR': 0.04, 'HAC': 144, 'HDG': 7, 'HGT': 1082, 'HPC': 0.4, 'HVN': 120, 'ICN': 0.55, 'ICO': 34, 'ICOS': 0.35, 'IND': 76, 'INDI': 790, 'ITS': 15.0012, 'IXT': 11, 'KBR': 143, 'KICK': 112, 'KMD': 4, 'LA': 41, 'LEND': 388, 'LAT': 1.44, 'LIFE': 13000, 'LRC': 27, 'LSK': 0.3, 'LOC': 11.076, 'LUN': 0.34, 'MAID': 5, 'MANA': 143, 'MCAP': 5.44, 'MIPS': 43, 'MNE': 1.33, 'MSP': 121, 'MCO': 0.357, 'MTH': 92, 'MYB': 3.9, 'NDC': 165, 'NEBL': 0.04, 'NET': 3.96, 'NTO': 998, 'NGC': 2.368, 'NXC': 13.39, 'NXT': 3, 'OAX': 15, 'ODN': 0.004, 'OMG': 2, 'OPT': 335, 'ORME': 2.8, 'OTN': 0.57, 'PAY': 3.1, 'PIX': 96, 'PLBT': 0.33, 'PLR': 114, 'PLU': 0.87, 'POE': 784, 'POLL': 3.5, 'PPT': 2, 'PRE': 32, 'PRG': 39, 'PRO': 41, 'PRS': 60, 'PTOY': 0.5, 'QAU': 63, 'QCN': 0.03, 'QTUM': 0.04, 'QVT': 64, 'REP': 0.02, 'RKC': 15, 'RLC': 1.21, 'RVT': 14, 'SC': 30, 'SAN': 2.24, 'SBD': 0.03, 'SCL': 2.6, 'SISA': 1640, 'SKIN': 407, 'SWFTC': 352.94, 'SMART': 0.4, 'SMS': 0.0375, 'SNC': 36, 'SNGLS': 4, 'SNM': 48, 'SNT': 233, 'STAR': 0.144, 'STORM': 153.19, 'STEEM': 0.01, 'STRAT': 0.01, 'SPF': 14.4, 'STU': 14, 'STX': 11, 'SUB': 17, 'SUR': 3, 'SWT': 0.51, 'TAAS': 0.91, 'TBT': 2.37, 'TFL': 15, 'TIME': 0.03, 'TIX': 7.1, 'TKN': 1, 'TGT': 173, 'TKR': 84, 'TNT': 90, 'TRST': 1.6, 'TRX': 270, 'UET': 480, 'UGT': 15, 'UTT': 3, 'VEN': 14, 'VERI': 0.037, 'VIB': 50, 'VIBE': 145, 'VOISE': 618, 'WEALTH': 0.0168, 'WINGS': 2.4, 'WTC': 0.75, 'WRC': 48, 'XAUR': 3.23, 'XDN': 0.01, 'XEM': 15, 'XUC': 0.9, 'YOYOW': 140, 'ZAP': 24, 'ZRX': 23, 'ZSC': 191, }, 'deposit': { 'BTC': 0, 'ETH': 0, 'BCH': 0, 'USDT': 0, 'BTG': 0, 'LTC': 0, 'ZEC': 0, 'XMR': 0, '1ST': 0, 'ADX': 0, 'AE': 0, 'AEON': 0, 'AIR': 0, 'AMP': 0, 'ANT': 0, 'ARDR': 0, 'ARN': 0, 'ART': 0, 'ATB': 0, 'ATL': 0, 'ATM': 0, 'ATS': 0, 'AVT': 0, 'BAS': 0, 'BCN': 0, 'BET': 0, 'BKB': 0, 'BMC': 0, 'BMT': 0, 'BNT': 0, 'BQX': 0, 'BTM': 0, 'BTX': 0, 'BUS': 0, 'CCT': 0, 'CDT': 0, 'CDX': 0, 'CFI': 0, 'CLD': 0, 'CND': 0, 'CNX': 0, 'COSS': 0, 'CSNO': 0, 'CTR': 0, 'CTX': 0, 'CVC': 0, 'DBIX': 0, 'DCN': 0, 'DCT': 0, 'DDF': 0, 'DENT': 0, 'DGB': 0, 'DGD': 0, 'DICE': 0, 'DLT': 0, 'DNT': 0, 'DOGE': 0, 'DOV': 0, 'DRPU': 0, 'DRT': 0, 'DSH': 0, 'EBET': 0, 'EBTC': 0, 'EBTCOLD': 0, 'ECAT': 0, 'EDG': 0, 'EDO': 0, 'ELE': 0, 'ELM': 0, 'EMC': 0, 'EMGO': 0, 'ENJ': 0, 'EOS': 0, 'ERO': 0, 'ETBS': 0, 'ETC': 0, 'ETP': 0, 'EVX': 0, 'EXN': 0, 'FRD': 0, 'FUEL': 0, 'FUN': 0, 'FYN': 0, 'FYP': 0, 'GNO': 0, 'GUP': 0, 'GVT': 0, 'HAC': 0, 'HDG': 0, 'HGT': 0, 'HPC': 0, 'HVN': 0, 'ICN': 0, 'ICO': 0, 'ICOS': 0, 'IND': 0, 'INDI': 0, 'ITS': 0, 'IXT': 0, 'KBR': 0, 'KICK': 0, 'LA': 0, 'LAT': 0, 'LIFE': 0, 'LRC': 0, 'LSK': 0, 'LUN': 0, 'MAID': 0, 'MANA': 0, 'MCAP': 0, 'MIPS': 0, 'MNE': 0, 'MSP': 0, 'MTH': 0, 'MYB': 0, 'NDC': 0, 'NEBL': 0, 'NET': 0, 'NTO': 0, 'NXC': 0, 'NXT': 0, 'OAX': 0, 'ODN': 0, 'OMG': 0, 'OPT': 0, 'ORME': 0, 'OTN': 0, 'PAY': 0, 'PIX': 0, 'PLBT': 0, 'PLR': 0, 'PLU': 0, 'POE': 0, 'POLL': 0, 'PPT': 0, 'PRE': 0, 'PRG': 0, 'PRO': 0, 'PRS': 0, 'PTOY': 0, 'QAU': 0, 'QCN': 0, 'QTUM': 0, 'QVT': 0, 'REP': 0, 'RKC': 0, 'RVT': 0, 'SAN': 0, 'SBD': 0, 'SCL': 0, 'SISA': 0, 'SKIN': 0, 'SMART': 0, 'SMS': 0, 'SNC': 0, 'SNGLS': 0, 'SNM': 0, 'SNT': 0, 'STEEM': 0, 'STRAT': 0, 'STU': 0, 'STX': 0, 'SUB': 0, 'SUR': 0, 'SWT': 0, 'TAAS': 0, 'TBT': 0, 'TFL': 0, 'TIME': 0, 'TIX': 0, 'TKN': 0, 'TKR': 0, 'TNT': 0, 'TRST': 0, 'TRX': 0, 'UET': 0, 'UGT': 0, 'VEN': 0, 'VERI': 0, 'VIB': 0, 'VIBE': 0, 'VOISE': 0, 'WEALTH': 0, 'WINGS': 0, 'WTC': 0, 'XAUR': 0, 'XDN': 0, 'XEM': 0, 'XUC': 0, 'YOYOW': 0, 'ZAP': 0, 'ZRX': 0, 'ZSC': 0, }, }, }, 'options': { 'defaultTimeInForce': 'FOK', }, 'commonCurrencies': { 'BET': 'DAO.Casino', 'CAT': 'BitClave', 'CPT': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 'DRK': 'DASH', 'EMGO': 'MGO', 'GET': 'Themis', 'HSR': 'HC', 'LNC': 'LinkerCoin', 'PLA': 'PlayChip', 'UNC': 'Unigame', 'USD': 'USDT', 'XBT': 'BTC', }, 'exceptions': { '504': RequestTimeout, // {"error":{"code":504,"message":"Gateway Timeout"}} '1002': AuthenticationError, // {"error":{"code":1002,"message":"Authorization failed","description":""}} '1003': PermissionDenied, // "Action is forbidden for this API key" '2010': InvalidOrder, // "Quantity not a valid number" '2001': BadSymbol, // "Symbol not found" '2011': InvalidOrder, // "Quantity too low" '2020': InvalidOrder, // "Price not a valid number" '20002': OrderNotFound, // canceling non-existent order '20001': InsufficientFunds, }, }); } feeToPrecision (symbol, fee) { return this.decimalToPrecision (fee, TRUNCATE, 8, DECIMAL_PLACES); } async fetchMarkets (params = {}) { const response = await this.publicGetSymbol (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'id'); const baseId = this.safeString (market, 'baseCurrency'); const quoteId = this.safeString (market, 'quoteCurrency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const lot = this.safeFloat (market, 'quantityIncrement'); const step = this.safeFloat (market, 'tickSize'); const precision = { 'price': this.precisionFromString (market['tickSize']), // FIXME: for lots > 1 the following line returns 0 // 'amount': this.precisionFromString (market['quantityIncrement']), 'amount': -1 * parseInt (Math.log10 (lot)), }; const taker = this.safeFloat (market, 'takeLiquidityRate'); const maker = this.safeFloat (market, 'provideLiquidityRate'); result.push (this.extend (this.fees['trading'], { 'info': market, 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'taker': taker, 'maker': maker, 'precision': precision, 'limits': { 'amount': { 'min': lot, 'max': undefined, }, 'price': { 'min': step, 'max': undefined, }, 'cost': { 'min': lot * step, 'max': undefined, }, }, })); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrency (params); const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'id'); // todo: will need to rethink the fees // to add support for multiple withdrawal/deposit methods and // differentiated fees for each particular method const precision = 8; // default precision, todo: fix "magic constants" const code = this.safeCurrencyCode (id); const payin = this.safeValue (currency, 'payinEnabled'); const payout = this.safeValue (currency, 'payoutEnabled'); const transfer = this.safeValue (currency, 'transferEnabled'); let active = payin && payout && transfer; if ('disabled' in currency) { if (currency['disabled']) { active = false; } } let type = 'fiat'; if (('crypto' in currency) && currency['crypto']) { type = 'crypto'; } const name = this.safeString (currency, 'fullName'); result[code] = { 'id': id, 'code': code, 'type': type, 'payin': payin, 'payout': payout, 'transfer': transfer, 'info': currency, 'name': name, 'active': active, 'fee': this.safeFloat (currency, 'payoutFee'), // todo: redesign 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': Math.pow (10, precision), }, }, }; } return result; } async fetchTradingFee (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = this.extend ({ 'symbol': market['id'], }, this.omit (params, 'symbol')); const response = await this.privateGetTradingFeeSymbol (request); // // { // takeLiquidityRate: '0.001', // provideLiquidityRate: '-0.0001' // } // return { 'info': response, 'maker': this.safeFloat (response, 'provideLiquidityRate'), 'taker': this.safeFloat (response, 'takeLiquidityRate'), }; } async fetchBalance (params = {}) { await this.loadMarkets (); const type = this.safeString (params, 'type', 'trading'); const method = 'privateGet' + this.capitalize (type) + 'Balance'; const query = this.omit (params, 'type'); const response = await this[method] (query); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'reserved'); result[code] = account; } return this.parseBalance (result); } parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { return [ this.parse8601 (this.safeString (ohlcv, 'timestamp')), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'max'), this.safeFloat (ohlcv, 'min'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'period': this.timeframes[timeframe], }; if (since !== undefined) { request['from'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetCandlesSymbol (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; if (limit !== undefined) { request['limit'] = limit; // default = 100, 0 = unlimited } const response = await this.publicGetOrderbookSymbol (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'size'); } parseTicker (ticker, market = undefined) { const timestamp = this.parse8601 (ticker['timestamp']); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const baseVolume = this.safeFloat (ticker, 'volume'); const quoteVolume = this.safeFloat (ticker, 'volumeQuote'); const open = this.safeFloat (ticker, 'open'); const last = this.safeFloat (ticker, 'last'); let change = undefined; let percentage = undefined; let average = undefined; if (last !== undefined && open !== undefined) { change = last - open; average = this.sum (last, open) / 2; if (open > 0) { percentage = change / open * 100; } } let vwap = undefined; if (quoteVolume !== undefined) { if (baseVolume !== undefined) { if (baseVolume > 0) { vwap = quoteVolume / baseVolume; } } } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker (params); const result = {}; for (let i = 0; i < response.length; i++) { const ticker = response[i]; const marketId = this.safeString (ticker, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { const market = this.markets_by_id[marketId]; const symbol = market['symbol']; result[symbol] = this.parseTicker (ticker, market); } else { result[marketId] = this.parseTicker (ticker); } } } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTickerSymbol (this.extend (request, params)); if ('message' in response) { throw new ExchangeError (this.id + ' ' + response['message']); } return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { // // createMarketOrder // // { fee: "0.0004644", // id: 386394956, // price: "0.4644", // quantity: "1", // timestamp: "2018-10-25T16:41:44.780Z" } // // fetchTrades ... // // fetchMyTrades ... // const timestamp = this.parse8601 (trade['timestamp']); let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = marketId; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrency = market ? market['quote'] : undefined; fee = { 'cost': feeCost, 'currency': feeCurrency, }; } // we use clientOrderId as the order id with HitBTC intentionally // because most of their endpoints will require clientOrderId // explained here: https://github.com/ccxt/ccxt/issues/5674 const orderId = this.safeString (trade, 'clientOrderId'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'quantity'); const cost = price * amount; const side = this.safeString (trade, 'side'); const id = this.safeString (trade, 'id'); return { 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = {}; if (code !== undefined) { currency = this.currency (code); request['asset'] = currency['id']; } if (since !== undefined) { request['startTime'] = since; } const response = await this.privateGetAccountTransactions (this.extend (request, params)); return this.parseTransactions (response, currency, since, limit); } parseTransaction (transaction, currency = undefined) { // // { // id: 'd53ee9df-89bf-4d09-886e-849f8be64647', // index: 1044718371, // type: 'payout', // status: 'success', // currency: 'ETH', // amount: '4.522683200000000000000000', // createdAt: '2018-06-07T00:43:32.426Z', // updatedAt: '2018-06-07T00:45:36.447Z', // hash: '0x973e5683dfdf80a1fb1e0b96e19085b6489221d2ddf864daa46903c5ec283a0f', // address: '0xC5a59b21948C1d230c8C54f05590000Eb3e1252c', // fee: '0.00958', // }, // { // id: 'e6c63331-467e-4922-9edc-019e75d20ba3', // index: 1044714672, // type: 'exchangeToBank', // status: 'success', // currency: 'ETH', // amount: '4.532263200000000000', // createdAt: '2018-06-07T00:42:39.543Z', // updatedAt: '2018-06-07T00:42:39.683Z', // }, // { // id: '3b052faa-bf97-4636-a95c-3b5260015a10', // index: 1009280164, // type: 'bankToExchange', // status: 'success', // currency: 'CAS', // amount: '104797.875800000000000000', // createdAt: '2018-05-19T02:34:36.750Z', // updatedAt: '2018-05-19T02:34:36.857Z', // }, // { // id: 'd525249f-7498-4c81-ba7b-b6ae2037dc08', // index: 1009279948, // type: 'payin', // status: 'success', // currency: 'CAS', // amount: '104797.875800000000000000', // createdAt: '2018-05-19T02:30:16.698Z', // updatedAt: '2018-05-19T02:34:28.159Z', // hash: '0xa6530e1231de409cf1f282196ed66533b103eac1df2aa4a7739d56b02c5f0388', // address: '0xd53ed559a6d963af7cb3f3fcd0e7ca499054db8b', // } // // { // "id": "4f351f4f-a8ee-4984-a468-189ed590ddbd", // "index": 3112719565, // "type": "withdraw", // "status": "success", // "currency": "BCHOLD", // "amount": "0.02423133", // "createdAt": "2019-07-16T16:52:04.494Z", // "updatedAt": "2019-07-16T16:54:07.753Z" // } const id = this.safeString (transaction, 'id'); const timestamp = this.parse8601 (this.safeString (transaction, 'createdAt')); const updated = this.parse8601 (this.safeString (transaction, 'updatedAt')); const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const amount = this.safeFloat (transaction, 'amount'); const address = this.safeString (transaction, 'address'); const txid = this.safeString (transaction, 'hash'); let fee = undefined; const feeCost = this.safeFloat (transaction, 'fee'); if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': code, }; } const type = this.parseTransactionType (this.safeString (transaction, 'type')); return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': undefined, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': updated, 'fee': fee, }; } parseTransactionStatus (status) { const statuses = { 'pending': 'pending', 'failed': 'failed', 'success': 'ok', }; return this.safeString (statuses, status, status); } parseTransactionType (type) { const types = { 'payin': 'deposit', 'payout': 'withdrawal', 'withdraw': 'withdrawal', }; return this.safeString (types, type, type); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['sort'] = 'ASC'; request['from'] = this.iso8601 (since); } const response = await this.publicGetTradesSymbol (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); // we use clientOrderId as the order id with HitBTC intentionally // because most of their endpoints will require clientOrderId // explained here: https://github.com/ccxt/ccxt/issues/5674 // their max accepted length is 32 characters const uuid = this.uuid (); const parts = uuid.split ('-'); let clientOrderId = parts.join (''); clientOrderId = clientOrderId.slice (0, 32); amount = parseFloat (amount); const request = { 'clientOrderId': clientOrderId, 'symbol': market['id'], 'side': side, 'quantity': this.amountToPrecision (symbol, amount), 'type': type, }; if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); } else { request['timeInForce'] = this.options['defaultTimeInForce']; } const response = await this.privatePostOrder (this.extend (request, params)); const order = this.parseOrder (response); if (order['status'] === 'rejected') { throw new InvalidOrder (this.id + ' order was rejected by the exchange ' + this.json (order)); } const id = order['id']; this.orders[id] = order; return order; } async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { await this.loadMarkets (); // we use clientOrderId as the order id with HitBTC intentionally // because most of their endpoints will require clientOrderId // explained here: https://github.com/ccxt/ccxt/issues/5674 // their max accepted length is 32 characters const uuid = this.uuid (); const parts = uuid.split ('-'); let requestClientId = parts.join (''); requestClientId = requestClientId.slice (0, 32); const request = { 'clientOrderId': id, 'requestClientId': requestClientId, }; if (amount !== undefined) { request['quantity'] = this.amountToPrecision (symbol, amount); } if (price !== undefined) { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePatchOrderClientOrderId (this.extend (request, params)); const order = this.parseOrder (response); this.orders[order['id']] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); // we use clientOrderId as the order id with HitBTC intentionally // because most of their endpoints will require clientOrderId // explained here: https://github.com/ccxt/ccxt/issues/5674 const request = { 'clientOrderId': id, }; const response = await this.privateDeleteOrderClientOrderId (this.extend (request, params)); return this.parseOrder (response); } parseOrderStatus (status) { const statuses = { 'new': 'open', 'suspended': 'open', 'partiallyFilled': 'open', 'filled': 'closed', 'canceled': 'canceled', 'expired': 'failed', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createMarketOrder // // { clientOrderId: "fe36aa5e190149bf9985fb673bbb2ea0", // createdAt: "2018-10-25T16:41:44.780Z", // cumQuantity: "1", // id: "66799540063", // quantity: "1", // side: "sell", // status: "filled", // symbol: "XRPUSDT", // timeInForce: "FOK", // tradesReport: [ { fee: "0.0004644", // id: 386394956, // price: "0.4644", // quantity: "1", // timestamp: "2018-10-25T16:41:44.780Z" } ], // type: "market", // updatedAt: "2018-10-25T16:41:44.780Z" } // const created = this.parse8601 (this.safeString (order, 'createdAt')); const updated = this.parse8601 (this.safeString (order, 'updatedAt')); const marketId = this.safeString (order, 'symbol'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = marketId; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['id']; } } const amount = this.safeFloat (order, 'quantity'); const filled = this.safeFloat (order, 'cumQuantity'); const status = this.parseOrderStatus (this.safeString (order, 'status')); // we use clientOrderId as the order id with HitBTC intentionally // because most of their endpoints will require clientOrderId // explained here: https://github.com/ccxt/ccxt/issues/5674 const id = this.safeString (order, 'clientOrderId'); const clientOrderId = id; let price = this.safeFloat (order, 'price'); if (price === undefined) { if (id in this.orders) { price = this.orders[id]['price']; } } let remaining = undefined; let cost = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = amount - filled; if (price !== undefined) { cost = filled * price; } } } const type = this.safeString (order, 'type'); const side = this.safeString (order, 'side'); let trades = this.safeValue (order, 'tradesReport'); let fee = undefined; let average = undefined; if (trades !== undefined) { trades = this.parseTrades (trades, market); let feeCost = undefined; const numTrades = trades.length; let tradesCost = 0; for (let i = 0; i < numTrades; i++) { if (feeCost === undefined) { feeCost = 0; } tradesCost = this.sum (tradesCost, trades[i]['cost']); const tradeFee = this.safeValue (trades[i], 'fee', {}); const tradeFeeCost = this.safeFloat (tradeFee, 'cost'); if (tradeFeeCost !== undefined) { feeCost = this.sum (feeCost, tradeFeeCost); } } cost = tradesCost; if ((filled !== undefined) && (filled > 0)) { average = cost / filled; if (type === 'market') { if (price === undefined) { price = average; } } } if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': market['quote'], }; } } return { 'id': id, 'clientOrderId': clientOrderId, // https://github.com/ccxt/ccxt/issues/5674 'timestamp': created, 'datetime': this.iso8601 (created), 'lastTradeTimestamp': updated, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'average': average, 'amount': amount, 'cost': cost, 'filled': filled, 'remaining': remaining, 'fee': fee, 'trades': trades, 'info': order, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); // we use clientOrderId as the order id with HitBTC intentionally // because most of their endpoints will require clientOrderId // explained here: https://github.com/ccxt/ccxt/issues/5674 const request = { 'clientOrderId': id, }; const response = await this.privateGetHistoryOrder (this.extend (request, params)); const numOrders = response.length; if (numOrders > 0) { return this.parseOrder (response[0]); } throw new OrderNotFound (this.id + ' order ' + id + ' not found'); } async fetchOpenOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); // we use clientOrderId as the order id with HitBTC intentionally // because most of their endpoints will require clientOrderId // explained here: https://github.com/ccxt/ccxt/issues/5674 const request = { 'clientOrderId': id, }; const response = await this.privateGetOrderClientOrderId (this.extend (request, params)); return this.parseOrder (response); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } const response = await this.privateGetOrder (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['from'] = this.iso8601 (since); } const response = await this.privateGetHistoryOrder (this.extend (request, params)); const parsedOrders = this.parseOrders (response, market); const orders = []; for (let i = 0; i < parsedOrders.length; i++) { const order = parsedOrders[i]; const status = order['status']; if ((status === 'closed') || (status === 'canceled')) { orders.push (order); } } return this.filterBySinceLimit (orders, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'symbol': 'BTC/USD', // optional // 'sort': 'DESC', // or 'ASC' // 'by': 'timestamp', // or 'id' String timestamp by default, or id // 'from': 'Datetime or Number', // ISO 8601 // 'till': 'Datetime or Number', // 'limit': 100, // 'offset': 0, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['from'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetHistoryTrades (this.extend (request, params)); // // [ // { // "id": 9535486, // "clientOrderId": "f8dbaab336d44d5ba3ff578098a68454", // "orderId": 816088377, // "symbol": "ETHBTC", // "side": "sell", // "quantity": "0.061", // "price": "0.045487", // "fee": "0.000002775", // "timestamp": "2017-05-17T12:32:57.848Z" // }, // { // "id": 9535437, // "clientOrderId": "27b9bfc068b44194b1f453c7af511ed6", // "orderId": 816088021, // "symbol": "ETHBTC", // "side": "buy", // "quantity": "0.038", // "price": "0.046000", // "fee": "-0.000000174", // "timestamp": "2017-05-17T12:30:57.848Z" // } // ] // return this.parseTrades (response, market, since, limit); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { // The id needed here is the exchange's id, and not the clientOrderID, // which is the id that is stored in the unified order id // To get the exchange's id you need to grab it from order['info']['id'] await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'id': id, }; const response = await this.privateGetHistoryOrderIdTrades (this.extend (request, params)); const numOrders = response.length; if (numOrders > 0) { return this.parseTrades (response, market, since, limit); } throw new OrderNotFound (this.id + ' order ' + id + ' not found, ' + this.id + '.fetchOrderTrades() requires an exchange-specific order id, you need to grab it from order["info"]["id"]'); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privatePostAccountCryptoAddressCurrency (this.extend (request, params)); const address = this.safeString (response, 'address'); this.checkAddress (address); const tag = this.safeString (response, 'paymentId'); return { 'currency': currency, 'address': address, 'tag': tag, 'info': response, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privateGetAccountCryptoAddressCurrency (this.extend (request, params)); const address = this.safeString (response, 'address'); this.checkAddress (address); const tag = this.safeString (response, 'paymentId'); return { 'currency': currency['code'], 'address': address, 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { await this.loadMarkets (); this.checkAddress (address); const currency = this.currency (code); const request = { 'currency': currency['id'], 'amount': parseFloat (amount), 'address': address, }; if (tag) { request['paymentId'] = tag; } const response = await this.privatePostAccountCryptoWithdraw (this.extend (request, params)); return { 'info': response, 'id': response['id'], }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = '/api/' + this.version + '/'; const query = this.omit (params, this.extractParams (path)); if (api === 'public') { url += api + '/' + this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); url += this.implodeParams (path, params); if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (Object.keys (query).length) { body = this.json (query); } const payload = this.encode (this.apiKey + ':' + this.secret); const auth = this.stringToBase64 (payload); headers = { 'Authorization': 'Basic ' + this.decode (auth), 'Content-Type': 'application/json', }; } url = this.urls['api'][api] + url; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } if (code >= 400) { const feedback = this.id + ' ' + body; // {"code":504,"message":"Gateway Timeout","description":""} if ((code === 503) || (code === 504)) { throw new ExchangeNotAvailable (feedback); } // fallback to default error handler on rate limit errors // {"code":429,"message":"Too many requests","description":"Too many requests"} if (code === 429) { return; } // {"error":{"code":20002,"message":"Order not found","description":""}} if (body[0] === '{') { if ('error' in response) { const errorCode = this.safeString (response['error'], 'code'); this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); const message = this.safeString (response['error'], 'message'); if (message === 'Duplicate clientOrderId') { throw new InvalidOrder (feedback); } } } throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],91:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadRequest, AuthenticationError, NetworkError, ArgumentsRequired, OrderNotFound, InsufficientFunds } = require ('./base/errors'); const { TICK_SIZE } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class hollaex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'hollaex', 'name': 'HollaEx', 'countries': [ 'KR' ], 'rateLimit': 333, 'version': 'v1', 'has': { 'CORS': false, 'fetchMarkets': true, 'fetchCurrencies': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchOrderBook': true, 'fetchOrderBooks': true, 'fetchTrades': true, 'fetchOHLCV': true, 'fetchBalance': true, 'createOrder': true, 'createLimitBuyOrder': true, 'createLimitSellOrder': true, 'createMarketBuyOrder': true, 'createMarketSellOrder': true, 'cancelOrder': true, 'cancelAllOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': false, 'fetchOpenOrder': true, 'fetchOrder': false, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTransactions': false, 'fetchOrders': false, 'fetchMyTrades': true, 'withdraw': true, 'fetchDepositAddress': true, }, 'timeframes': { '1h': '1h', '1d': '1d', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/75841031-ca375180-5ddd-11ea-8417-b975674c23cb.jpg', 'api': 'https://api.hollaex.com', 'www': 'https://hollaex.com', 'doc': 'https://apidocs.hollaex.com', 'referral': 'https://pro.hollaex.com/signup?affiliation_code=QSWA6G', }, 'precisionMode': TICK_SIZE, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'api': { 'public': { 'get': [ 'health', 'constant', 'ticker', 'ticker/all', 'orderbooks', 'trades', 'chart', // TradingView data 'udf/config', 'udf/history', 'udf/symbols', ], }, 'private': { 'get': [ 'user', 'user/balance', 'user/trades', 'user/orders', 'user/orders/{order_id}', 'user/deposits', 'user/withdrawals', 'user/withdraw/{currency}/fee', ], 'post': [ 'user/request-withdrawal', 'order', ], 'delete': [ 'user/orders', 'user/orders/{order_id}', ], }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, }, }, 'exceptions': { 'broad': { 'Invalid token': AuthenticationError, 'Order not found': OrderNotFound, 'Insufficient balance': InsufficientFunds, }, 'exact': { '400': BadRequest, '403': AuthenticationError, '404': BadRequest, '405': BadRequest, '410': BadRequest, '429': BadRequest, '500': NetworkError, '503': NetworkError, }, }, 'options': { // how many seconds before the authenticated request expires 'api-expires': parseInt (this.timeout / 1000), }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetConstant (params); // // { // coins: { // xmr: { // id: 7, // fullname: "Monero", // symbol: "xmr", // active: true, // allow_deposit: true, // allow_withdrawal: true, // withdrawal_fee: 0.02, // min: 0.001, // max: 100000, // increment_unit: 0.001, // deposit_limits: { '1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0 }, // withdrawal_limits: { '1': 10, '2': 15, '3': 100, '4': 100, '5': 200, '6': 300, '7': 350, '8': 400, '9': 500, '10': -1 }, // created_at: "2019-12-09T07:14:02.720Z", // updated_at: "2020-01-16T12:12:53.162Z" // }, // // ... // }, // pairs: { // 'btc-usdt': { // id: 2, // name: "btc-usdt", // pair_base: "btc", // pair_2: "usdt", // taker_fees: { '1': 0.3, '2': 0.25, '3': 0.2, '4': 0.18, '5': 0.1, '6': 0.09, '7': 0.08, '8': 0.06, '9': 0.04, '10': 0 }, // maker_fees: { '1': 0.1, '2': 0.08, '3': 0.05, '4': 0.03, '5': 0, '6': 0, '7': 0, '8': 0, '9': 0, '10': 0 }, // min_size: 0.0001, // max_size: 1000, // min_price: 100, // max_price: 100000, // increment_size: 0.0001, // increment_price: 0.05, // active: true, // created_at: "2019-12-09T07:15:54.537Z", // updated_at: "2019-12-09T07:15:54.537Z" // }, // }, // config: { tiers: 10 }, // status: true // } // const pairs = this.safeValue (response, 'pairs', {}); const keys = Object.keys (pairs); const result = []; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const market = pairs[key]; const id = this.safeString (market, 'name'); const baseId = this.safeString (market, 'pair_base'); const quoteId = this.safeString (market, 'pair_2'); const base = this.commonCurrencyCode (baseId.toUpperCase ()); const quote = this.commonCurrencyCode (quoteId.toUpperCase ()); const symbol = base + '/' + quote; const active = this.safeValue (market, 'active'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': { 'price': this.safeFloat (market, 'increment_price'), 'amount': this.safeFloat (market, 'increment_size'), }, 'limits': { 'amount': { 'min': this.safeFloat (market, 'min_size'), 'max': this.safeFloat (market, 'max_size'), }, 'price': { 'min': this.safeFloat (market, 'min_price'), 'max': this.safeFloat (market, 'max_price'), }, 'cost': { 'min': undefined, 'max': undefined }, }, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetConstant (params); const coins = this.safeValue (response, 'coins', {}); const keys = Object.keys (coins); const result = {}; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const currency = coins[key]; const id = this.safeString (currency, 'symbol'); const numericId = this.safeInteger (currency, 'id'); const code = this.safeCurrencyCode (id); const name = this.safeString (currency, 'fullname'); const active = this.safeValue (currency, 'active'); const fee = this.safeFloat (currency, 'withdrawal_fee'); const precision = this.safeFloat (currency, 'increment_unit'); const withdrawalLimits = this.safeValue (currency, 'withdrawal_limits', []); result[code] = { 'id': id, 'numericId': numericId, 'code': code, 'info': currency, 'name': name, 'active': active, 'fee': fee, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (currency, 'min'), 'max': this.safeFloat (currency, 'max'), }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': this.safeValue (withdrawalLimits, 0), }, }, }; } return result; } async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetOrderbooks (params); const result = {}; const marketIds = Object.keys (response); for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const orderbook = response[marketId]; let symbol = marketId; if (marketId in this.markets_by_id) { const market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } const timestamp = this.parse8601 (this.safeString (orderbook, 'timestamp')); result[symbol] = this.parseOrderBook (response[marketId], timestamp); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const marketId = this.marketId (symbol); const request = { 'symbol': marketId, }; const response = await this.publicGetOrderbooks (this.extend (request, params)); // // { // "btc-usdt": { // "bids": [ // [ 8836.4, 1.022 ], // [ 8800, 0.0668 ], // [ 8797.75, 0.2398 ], // ], // "asks": [ // [ 8839.35, 1.5334 ], // [ 8852.6, 0.0579 ], // [ 8860.45, 0.1815 ], // ], // "timestamp": "2020-03-03T02:27:25.147Z" // }, // "eth-usdt": {}, // // ... // } // const orderbook = this.safeValue (response, marketId); const timestamp = this.parse8601 (this.safeString (orderbook, 'timestamp')); return this.parseOrderBook (orderbook, timestamp); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTicker (this.extend (request, params)); // // { // open: 8615.55, // close: 8841.05, // high: 8921.1, // low: 8607, // last: 8841.05, // volume: 20.2802, // timestamp: '2020-03-03T03:11:18.964Z' // } // return this.parseTicker (response, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickerAll (this.extend (params)); // // { // "bch-usdt": { // "time": "2020-03-02T04:29:45.011Z", // "open": 341.65, // "close":337.9, // "high":341.65, // "low":337.3, // "last":337.9, // "volume":0.054, // "symbol":"bch-usdt" // }, // // ... // } // return this.parseTickers (response, symbols); } parseTickers (response, symbols = undefined) { const result = {}; const keys = Object.keys (response); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const ticker = response[key]; let symbol = key; let market = undefined; const marketId = this.safeString (ticker, 'symbol', key); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } result[symbol] = this.parseTicker (ticker, market); } return this.filterByArray (result, 'symbol', symbols); } parseTicker (ticker, market = undefined) { // // fetchTicker // // { // open: 8615.55, // close: 8841.05, // high: 8921.1, // low: 8607, // last: 8841.05, // volume: 20.2802, // timestamp: '2020-03-03T03:11:18.964Z', // } // // fetchTickers // // { // "time": "2020-03-02T04:29:45.011Z", // "open": 341.65, // "close": 337.9, // "high": 341.65, // "low": 337.3, // "last": 337.9, // "volume": 0.054, // "symbol": "bch-usdt" // } // let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString2 (ticker, 'time', 'timestamp')); const close = this.safeFloat (ticker, 'close'); const result = { 'symbol': symbol, 'info': ticker, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': close, 'last': this.safeFloat (ticker, 'last', close), 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, }; return result; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); // // { // "btc-usdt": [ // { // "size": 0.5, // "price": 8830, // "side": "buy", // "timestamp": "2020-03-03T04:44:33.034Z" // }, // // ... // ] // } // const trades = await this.safeValue (response, market['id'], []); return this.parseTrades (trades, market, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "size": 0.5, // "price": 8830, // "side": "buy", // "timestamp": "2020-03-03T04:44:33.034Z" // } // // fetchMyTrades (private) // // { // "side": "buy", // "symbol": "eth-usdt", // "size": 0.086, // "price": 226.19, // "timestamp": "2020-03-03T08:03:55.459Z", // "fee": 0.1 // } // let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); let quote = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const datetime = this.safeString (trade, 'timestamp'); const timestamp = this.parse8601 (datetime); const side = this.safeString (trade, 'side'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'size'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } const feeCost = this.safeFloat (trade, 'fee'); let fee = undefined; if (feeCost !== undefined) { const feeCurrencyCode = (market !== undefined) ? market['quote'] : quote; fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } const result = { 'info': trade, 'id': undefined, 'timestamp': timestamp, 'datetime': datetime, 'symbol': symbol, 'order': undefined, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; return result; } async fetchOHLCV (symbol, timeframe = '1h', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'resolution': this.timeframes[timeframe], }; const duration = this.parseTimeframe (timeframe); if (since === undefined) { if (limit === undefined) { throw new ArgumentsRequired (this.id + " fetchOHLCV requires a 'since' or a 'limit' argument"); } else { const end = this.seconds (); const start = end - duration * limit; request['to'] = end; request['from'] = start; } } else { if (limit === undefined) { request['from'] = parseInt (since / 1000); request['to'] = this.seconds (); } else { const start = parseInt (since / 1000); request['from'] = start; request['to'] = this.sum (start, duration * limit); } } const response = await this.publicGetChart (this.extend (request, params)); // // [ // { // "time":"2020-03-02T20:00:00.000Z", // "close":8872.1, // "high":8872.1, // "low":8858.6, // "open":8858.6, // "symbol":"btc-usdt", // "volume":1.2922 // }, // ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } parseOHLCV (response, market = undefined, timeframe = '1h', since = undefined, limit = undefined) { // // { // "time":"2020-03-02T20:00:00.000Z", // "close":8872.1, // "high":8872.1, // "low":8858.6, // "open":8858.6, // "symbol":"btc-usdt", // "volume":1.2922 // } // return [ this.parse8601 (this.safeString (response, 'time')), this.safeFloat (response, 'open'), this.safeFloat (response, 'high'), this.safeFloat (response, 'low'), this.safeFloat (response, 'close'), this.safeFloat (response, 'volume'), ]; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetUserBalance (params); // // { // "updated_at": "2020-03-02T22:27:38.428Z", // "btc_balance": 0, // "btc_pending": 0, // "btc_available": 0, // "eth_balance": 0, // "eth_pending": 0, // "eth_available": 0, // // ... // } // const result = { 'info': response }; const currencyIds = Object.keys (this.currencies_by_id); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (response, currencyId + '_available'); account['total'] = this.safeFloat (response, currencyId + '_balance'); result[code] = account; } return this.parseBalance (result); } async fetchOpenOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privateGetUserOrdersOrderId (this.extend (request, params)); // // { // "created_at": "2018-03-23T04:14:08.663Z", // "title": "string", // "side": "sell", // "type": "limit", // "price": 0, // "size": 0, // "symbol": "xht-usdt", // "id": "string", // "created_by": 1, // "filled": 0 // } // return this.parseOrder (response); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } const response = await this.privateGetUserOrders (this.extend (request, params)); // // [ // { // "created_at":"2020-03-03T08:02:18.639Z", // "title":"5419ff3f-9d25-4af7-bcc2-803926518d76", // "side":"buy", // "type":"limit", // "price":226.19, // "size":0.086, // "symbol":"eth-usdt", // "id":"5419ff3f-9d25-4af7-bcc2-803926518d76", // "created_by":620, // "filled":0 // } // ] // return this.parseOrders (response, market); } parseOrder (order, market = undefined) { // // fetchOpenOrder, fetchOpenOrders // // { // "created_at":"2020-03-03T08:02:18.639Z", // "title":"5419ff3f-9d25-4af7-bcc2-803926518d76", // "side":"buy", // "type":"limit", // "price":226.19, // "size":0.086, // "symbol":"eth-usdt", // "id":"5419ff3f-9d25-4af7-bcc2-803926518d76", // "created_by":620, // "filled":0 // } // let symbol = undefined; const marketId = this.safeString (order, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const id = this.safeString (order, 'id'); const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const type = this.safeString (order, 'type'); const side = this.safeString (order, 'side'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'size'); const filled = this.safeFloat (order, 'filled'); let cost = undefined; let remaining = undefined; if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; } if (price !== undefined) { cost = filled * price; } } const status = (type === 'market') ? 'closed' : 'open'; const result = { 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'filled': filled, 'remaining': remaining, 'cost': cost, 'trades': undefined, 'fee': undefined, 'info': order, 'average': undefined, }; return result; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const order = { 'symbol': market['id'], 'side': side, 'size': amount, 'type': type, }; if (type !== 'market') { order['price'] = price; } const response = await this.privatePostOrder (this.extend (order, params)); // // { // "symbol": "xht-usdt", // "side": "sell", // "size": 1, // "type": "limit", // "price": 0.1, // "id": "string", // "created_by": 34, // "filled": 0, // "status": "pending" // } // return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; const response = await this.privateDeleteUserOrdersOrderId (this.extend (request, params)); // // { // "title": "string", // "symbol": "xht-usdt", // "side": "sell", // "size": 1, // "type": "limit", // "price": 0.1, // "id": "string", // "created_by": 34, // "filled": 0 // } // return this.parseOrder (response); } async cancelAllOrders (symbol = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.markets (symbol); request['symbol'] = market['id']; } const response = await this.privateDeleteUserOrders (this.extend (request, params)); // // [ // { // "title": "string", // "symbol": "xht-usdt", // "side": "sell", // "size": 1, // "type": "limit", // "price": 0.1, // "id": "string", // "created_by": 34, // "filled": 0 // } // ] // return this.parseOrders (response, market); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'symbol': market['id'], // 'limit': 50, // default 50, max 100 // 'page': 1, // page of data to retrieve // 'order_by': 'timestamp', // field to order data // 'order': 'asc', // asc or desc // 'start_date': 123, // starting date of queried data // 'end_date': 321, // ending date of queried data }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; // default 50, max 100 } if (since !== undefined) { request['start_date'] = this.iso8601 (since); } const response = await this.privateGetUserTrades (this.extend (request, params)); // // { // "count": 1, // "data": [ // { // "side": "buy", // "symbol": "eth-usdt", // "size": 0.086, // "price": 226.19, // "timestamp": "2020-03-03T08:03:55.459Z", // "fee": 0.1 // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTrades (data, market, since, limit); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const response = await this.privateGetUser (params); // // { // "id": 620, // "email": "email@gmail.com", // "full_name": "", // "name_verified": false, // "gender": false, // "nationality": "", // "phone_number": "", // "address": { "city": "", "address": "", "country": "", "postal_code": "" }, // "id_data": { "note": "", "type": "", "number": "", "status": 0 }, // "bank_account":[], // "crypto_wallet":{ // "xrp": "rJtoECs6rPkJoAfgtR8SDDshV6hRHe3X7y:391496555" // "usdt":"0x1fb4248e167901dfa0d8cdda2243a2126d7ce48d" // // ... // }, // "verification_level": 1, // "otp_enabled": true, // "activated": true, // "note": "", // "username": "user", // "affiliation_code": "QSWA6G", // "settings": { // "chat": { "set_username": false }, // "risk": { "order_portfolio_percentage": 20 }, // "audio": { // "public_trade": false, // "order_completed": true, // "order_partially_completed": true // }, // "language": "en", // "interface": { "theme": "white","order_book_levels": 10 }, // "notification": { // "popup_order_completed": true, // "popup_order_confirmation": true, // "popup_order_partially_filled": true // } // }, // "flagged": false, // "is_hap": false, // "pin": false, // "discount": 0, // "created_at": "2020-03-02T22:27:38.331Z", // "updated_at": "2020-03-03T07:54:58.315Z", // "balance": { // "xht_balance": 0, // "xht_pending": 0, // "xht_available": 0, // // ... // "updated_at": "2020-03-03T10:21:05.430Z" // }, // "images": [], // "fees": { // "btc-usdt": { "maker_fee": 0.1, "taker_fee": 0.3 }, // "eth-usdt": { "maker_fee": 0.1, "taker_fee": 0.3 }, // // ... // } // } // const cryptoWallet = this.safeValue (response, 'crypto_wallet'); let address = this.safeString (cryptoWallet, currency['id']); let tag = undefined; if (address !== undefined) { const parts = address.split (':'); address = this.safeString (parts, 0); tag = this.safeString (parts, 1); } this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'currency': currency['id'], // 'limit': 50, // default 50, max 100 // 'page': 1, // page of data to retrieve // 'order_by': 'timestamp', // field to order data // 'order': 'asc', // asc or desc // 'start_date': 123, // starting date of queried data // 'end_date': 321, // ending date of queried data }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (limit !== undefined) { request['limit'] = limit; // default 50, max 100 } if (since !== undefined) { request['start_date'] = this.iso8601 (since); } const response = await this.privateGetUserDeposits (this.extend (request, params)); // // { // "count": 1, // "data": [ // { // "id": 539, // "amount": 20, // "fee": 0, // "address": "0x5c0cc98270d7089408fcbcc8e2131287f5be2306", // "transaction_id": "0xd4006327a5ec2c41adbdcf566eaaba6597c3d45906abe78ea1a4a022647c2e28", // "status": true, // "dismissed": false, // "rejected": false, // "description": "", // "type": "deposit", // "currency": "usdt", // "created_at": "2020-03-03T07:56:36.198Z", // "updated_at": "2020-03-03T08:00:05.674Z", // "user_id": 620 // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTransactions (data, currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'currency': currency['id'], // 'limit': 50, // default 50, max 100 // 'page': 1, // page of data to retrieve // 'order_by': 'timestamp', // field to order data // 'order': 'asc', // asc or desc // 'start_date': 123, // starting date of queried data // 'end_date': 321, // ending date of queried data }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (limit !== undefined) { request['limit'] = limit; // default 50, max 100 } if (since !== undefined) { request['start_date'] = this.iso8601 (since); } const response = await this.privateGetUserWithdrawals (this.extend (request, params)); // // { // "count": 1, // "data": [ // { // "id": 539, // "amount": 20, // "fee": 0, // "address": "0x5c0cc98270d7089408fcbcc8e2131287f5be2306", // "transaction_id": "0xd4006327a5ec2c41adbdcf566eaaba6597c3d45906abe78ea1a4a022647c2e28", // "status": true, // "dismissed": false, // "rejected": false, // "description": "", // "type": "withdrawal", // "currency": "usdt", // "created_at": "2020-03-03T07:56:36.198Z", // "updated_at": "2020-03-03T08:00:05.674Z", // "user_id": 620 // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseTransactions (data, currency, since, limit); } parseTransaction (transaction, currency = undefined) { // // { // "id": 539, // "amount": 20, // "fee": 0, // "address": "0x5c0cc98270d7089408fcbcc8e2131287f5be2306", // "transaction_id": "0xd4006327a5ec2c41adbdcf566eaaba6597c3d45906abe78ea1a4a022647c2e28", // "status": true, // "dismissed": false, // "rejected": false, // "description": "", // "type": "withdrawal", // "currency": "usdt", // "created_at": "2020-03-03T07:56:36.198Z", // "updated_at": "2020-03-03T08:00:05.674Z", // "user_id": 620 // } // const id = this.safeString (transaction, 'id'); const txid = this.safeString (transaction, 'transaction_id'); const timestamp = this.parse8601 (this.safeString (transaction, 'created_at')); const updated = this.parse8601 (this.safeString (transaction, 'updated_at')); const type = this.safeString (transaction, 'type'); const amount = this.safeFloat (transaction, 'amount'); let address = this.safeString (transaction, 'address'); let addressTo = undefined; const addressFrom = undefined; let tag = undefined; let tagTo = undefined; const tagFrom = undefined; if (address !== undefined) { const parts = address.split (':'); address = this.safeString (parts, 0); tag = this.safeString (parts, 1); addressTo = address; tagTo = tag; } const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId); let status = this.safeValue (transaction, 'status'); const dismissed = this.safeValue (transaction, 'dismissed'); const rejected = this.safeValue (transaction, 'rejected'); if (status) { status = 'ok'; } else if (dismissed) { status = 'canceled'; } else if (rejected) { status = 'failed'; } else { status = 'pending'; } const fee = { 'currency': code, 'cost': this.safeFloat (transaction, 'fee'), }; return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'addressFrom': addressFrom, 'address': address, 'addressTo': addressTo, 'tagFrom': tagFrom, 'tag': tag, 'tagTo': tagTo, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': updated, 'fee': fee, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); if (tag !== undefined) { address += ':' + tag; } const request = { 'currency': currency['id'], 'amount': amount, 'address': address, }; // one time password let otp = this.safeString (params, 'otp_code'); if ((otp !== undefined) || (this.twofa !== undefined)) { if (otp === undefined) { otp = this.oath (); } request['otp_code'] = otp; } const response = await this.privatePostUserRequestWithdrawal (this.extend (request, params)); return { 'info': response, 'id': undefined, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const query = this.omit (params, this.extractParams (path)); path = '/' + this.version + '/' + this.implodeParams (path, params); if (method === 'GET') { if (Object.keys (query).length) { path += '?' + this.urlencode (query); } } const url = this.urls['api'] + path; if (api === 'private') { this.checkRequiredCredentials (); const defaultExpires = this.safeInteger2 (this.options, 'api-expires', 'expires', parseInt (this.timeout / 1000)); const expires = this.sum (this.seconds (), defaultExpires); const expiresString = expires.toString (); let auth = method + path + expiresString; headers = { 'api-key': this.encode (this.apiKey), 'api-expires': expiresString, }; if (method === 'POST') { headers['Content-type'] = 'application/json'; if (Object.keys (query).length) { body = this.json (query); auth += body; } } const signature = this.hmac (this.encode (auth), this.encode (this.secret)); headers['api-signature'] = signature; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } if ((code >= 400) && (code <= 503)) { // // { "message": "Invalid token" } // const feedback = this.id + ' ' + body; const message = this.safeString (response, 'message'); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); const status = code.toString (); this.throwExactlyMatchedException (this.exceptions['exact'], status, feedback); } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],92:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { AuthenticationError, ExchangeError, PermissionDenied, ExchangeNotAvailable, InvalidOrder, OrderNotFound, InsufficientFunds, ArgumentsRequired, BadSymbol, BadRequest, RequestTimeout } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class huobipro extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'huobipro', 'name': 'Huobi Pro', 'countries': [ 'CN' ], 'rateLimit': 2000, 'userAgent': this.userAgents['chrome39'], 'version': 'v1', 'accounts': undefined, 'accountsById': undefined, 'hostname': 'api.huobi.pro', // api.testnet.huobi.pro 'pro': true, 'has': { 'CORS': false, 'fetchTickers': true, 'fetchDepositAddress': true, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchTradingLimits': true, 'fetchMyTrades': true, 'withdraw': true, 'fetchCurrencies': true, 'fetchDeposits': true, 'fetchWithdrawals': true, }, 'timeframes': { '1m': '1min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '60min', '4h': '4hour', '1d': '1day', '1w': '1week', '1M': '1mon', '1y': '1year', }, 'urls': { 'test': { 'market': 'https://api.testnet.huobi.pro', 'public': 'https://api.testnet.huobi.pro', 'private': 'https://api.testnet.huobi.pro', }, 'logo': 'https://user-images.githubusercontent.com/1294454/76137448-22748a80-604e-11ea-8069-6e389271911d.jpg', 'api': { 'market': 'https://{hostname}', 'public': 'https://{hostname}', 'private': 'https://{hostname}', 'v2Public': 'https://{hostname}', 'v2Private': 'https://{hostname}', }, 'www': 'https://www.huobi.pro', 'referral': 'https://www.huobi.co/en-us/topic/invited/?invite_code=rwrd3', 'doc': 'https://huobiapi.github.io/docs/spot/v1/cn/', 'fees': 'https://www.huobi.pro/about/fee/', }, 'api': { 'v2Public': { 'get': [ 'reference/currencies', ], }, 'v2Private': { 'get': [ 'account/withdraw/quota', 'account/deposit/address', 'reference/transact-fee-rate', ], 'post': [ 'sub-user/management', ], }, 'market': { 'get': [ 'history/kline', // 获取K线数据 'detail/merged', // 获取聚合行情(Ticker) 'depth', // 获取 Market Depth 数据 'trade', // 获取 Trade Detail 数据 'history/trade', // 批量获取最近的交易记录 'detail', // 获取 Market Detail 24小时成交量数据 'tickers', ], }, 'public': { 'get': [ 'common/symbols', // 查询系统支持的所有交易对 'common/currencys', // 查询系统支持的所有币种 'common/timestamp', // 查询系统当前时间 'common/exchange', // order limits 'settings/currencys', // ?language=en-US ], }, 'private': { 'get': [ 'account/accounts', // 查询当前用户的所有账户(即account-id) 'account/accounts/{id}/balance', // 查询指定账户的余额 'account/accounts/{sub-uid}', 'account/history', 'cross-margin/loan-info', 'fee/fee-rate/get', 'order/openOrders', 'order/orders', 'order/orders/{id}', // 查询某个订单详情 'order/orders/{id}/matchresults', // 查询某个订单的成交明细 'order/orders/getClientOrder', 'order/history', // 查询当前委托、历史委托 'order/matchresults', // 查询当前成交、历史成交 'dw/withdraw-virtual/addresses', // 查询虚拟币提现地址 'query/deposit-withdraw', 'margin/loan-orders', // 借贷订单 'margin/accounts/balance', // 借贷账户详情 'points/actions', 'points/orders', 'subuser/aggregate-balance', 'stable-coin/exchange_rate', 'stable-coin/quote', ], 'post': [ 'futures/transfer', 'order/batch-orders', 'order/orders/place', // 创建并执行一个新订单 (一步下单, 推荐使用) 'order/orders/submitCancelClientOrder', 'order/orders/batchCancelOpenOrders', 'order/orders', // 创建一个新的订单请求 (仅创建订单,不执行下单) 'order/orders/{id}/place', // 执行一个订单 (仅执行已创建的订单) 'order/orders/{id}/submitcancel', // 申请撤销一个订单请求 'order/orders/batchcancel', // 批量撤销订单 'dw/balance/transfer', // 资产划转 'dw/withdraw/api/create', // 申请提现虚拟币 'dw/withdraw-virtual/create', // 申请提现虚拟币 'dw/withdraw-virtual/{id}/place', // 确认申请虚拟币提现 'dw/withdraw-virtual/{id}/cancel', // 申请取消提现虚拟币 'dw/transfer-in/margin', // 现货账户划入至借贷账户 'dw/transfer-out/margin', // 借贷账户划出至现货账户 'margin/orders', // 申请借贷 'margin/orders/{id}/repay', // 归还借贷 'stable-coin/exchange', 'subuser/transfer', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.002, 'taker': 0.002, }, }, 'exceptions': { 'exact': { // err-code 'bad-request': BadRequest, 'api-not-support-temp-addr': PermissionDenied, // {"status":"error","err-code":"api-not-support-temp-addr","err-msg":"API withdrawal does not support temporary addresses","data":null} 'timeout': RequestTimeout, // {"ts":1571653730865,"status":"error","err-code":"timeout","err-msg":"Request Timeout"} 'gateway-internal-error': ExchangeNotAvailable, // {"status":"error","err-code":"gateway-internal-error","err-msg":"Failed to load data. Try again later.","data":null} 'account-frozen-balance-insufficient-error': InsufficientFunds, // {"status":"error","err-code":"account-frozen-balance-insufficient-error","err-msg":"trade account balance is not enough, left: `0.0027`","data":null} 'invalid-amount': InvalidOrder, // eg "Paramemter `amount` is invalid." 'order-limitorder-amount-min-error': InvalidOrder, // limit order amount error, min: `0.001` 'order-limitorder-amount-max-error': InvalidOrder, // market order amount error, max: `1000000` 'order-marketorder-amount-min-error': InvalidOrder, // market order amount error, min: `0.01` 'order-limitorder-price-min-error': InvalidOrder, // limit order price error 'order-limitorder-price-max-error': InvalidOrder, // limit order price error 'order-orderstate-error': OrderNotFound, // canceling an already canceled order 'order-queryorder-invalid': OrderNotFound, // querying a non-existent order 'order-update-error': ExchangeNotAvailable, // undocumented error 'api-signature-check-failed': AuthenticationError, 'api-signature-not-valid': AuthenticationError, // {"status":"error","err-code":"api-signature-not-valid","err-msg":"Signature not valid: Incorrect Access key [Access key错误]","data":null} 'base-record-invalid': OrderNotFound, // https://github.com/ccxt/ccxt/issues/5750 // err-msg 'invalid symbol': BadSymbol, // {"ts":1568813334794,"status":"error","err-code":"invalid-parameter","err-msg":"invalid symbol"} 'invalid-parameter': BadRequest, // {"ts":1576210479343,"status":"error","err-code":"invalid-parameter","err-msg":"symbol trade not open now"} 'base-symbol-trade-disabled': BadSymbol, // {"status":"error","err-code":"base-symbol-trade-disabled","err-msg":"Trading is disabled for this symbol","data":null} }, }, 'options': { // https://github.com/ccxt/ccxt/issues/5376 'fetchOrdersByStatesMethod': 'private_get_order_orders', // 'private_get_order_history' // https://github.com/ccxt/ccxt/pull/5392 'fetchOpenOrdersMethod': 'fetch_open_orders_v1', // 'fetch_open_orders_v2' // https://github.com/ccxt/ccxt/issues/5388 'createMarketBuyOrderRequiresPrice': true, 'fetchMarketsMethod': 'publicGetCommonSymbols', 'fetchBalanceMethod': 'privateGetAccountAccountsIdBalance', 'createOrderMethod': 'privatePostOrderOrdersPlace', 'language': 'en-US', }, 'commonCurrencies': { // https://github.com/ccxt/ccxt/issues/6081 // https://github.com/ccxt/ccxt/issues/3365 // https://github.com/ccxt/ccxt/issues/2873 'GET': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol) 'HOT': 'Hydro Protocol', // conflict with HOT (Holo) https://github.com/ccxt/ccxt/issues/4929 }, }); } async fetchTradingLimits (symbols = undefined, params = {}) { // this method should not be called directly, use loadTradingLimits () instead // by default it will try load withdrawal fees of all currencies (with separate requests) // however if you define symbols = [ 'ETH/BTC', 'LTC/BTC' ] in args it will only load those await this.loadMarkets (); if (symbols === undefined) { symbols = this.symbols; } const result = {}; for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; result[symbol] = await this.fetchTradingLimitsById (this.marketId (symbol), params); } return result; } async fetchTradingLimitsById (id, params = {}) { const request = { 'symbol': id, }; const response = await this.publicGetCommonExchange (this.extend (request, params)); // // { status: "ok", // data: { symbol: "aidocbtc", // 'buy-limit-must-less-than': 1.1, // 'sell-limit-must-greater-than': 0.9, // 'limit-order-must-greater-than': 1, // 'limit-order-must-less-than': 5000000, // 'market-buy-order-must-greater-than': 0.0001, // 'market-buy-order-must-less-than': 100, // 'market-sell-order-must-greater-than': 1, // 'market-sell-order-must-less-than': 500000, // 'circuit-break-when-greater-than': 10000, // 'circuit-break-when-less-than': 10, // 'market-sell-order-rate-must-less-than': 0.1, // 'market-buy-order-rate-must-less-than': 0.1 } } // return this.parseTradingLimits (this.safeValue (response, 'data', {})); } parseTradingLimits (limits, symbol = undefined, params = {}) { // // { symbol: "aidocbtc", // 'buy-limit-must-less-than': 1.1, // 'sell-limit-must-greater-than': 0.9, // 'limit-order-must-greater-than': 1, // 'limit-order-must-less-than': 5000000, // 'market-buy-order-must-greater-than': 0.0001, // 'market-buy-order-must-less-than': 100, // 'market-sell-order-must-greater-than': 1, // 'market-sell-order-must-less-than': 500000, // 'circuit-break-when-greater-than': 10000, // 'circuit-break-when-less-than': 10, // 'market-sell-order-rate-must-less-than': 0.1, // 'market-buy-order-rate-must-less-than': 0.1 } // return { 'info': limits, 'limits': { 'amount': { 'min': this.safeFloat (limits, 'limit-order-must-greater-than'), 'max': this.safeFloat (limits, 'limit-order-must-less-than'), }, }, }; } async fetchMarkets (params = {}) { const method = this.options['fetchMarketsMethod']; const response = await this[method] (params); const markets = this.safeValue (response, 'data'); const numMarkets = markets.length; if (numMarkets < 1) { throw new ExchangeError (this.id + ' publicGetCommonSymbols returned empty response: ' + this.json (markets)); } const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const baseId = this.safeString (market, 'base-currency'); const quoteId = this.safeString (market, 'quote-currency'); const id = baseId + quoteId; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': market['amount-precision'], 'price': market['price-precision'], }; const maker = (base === 'OMG') ? 0 : 0.2 / 100; const taker = (base === 'OMG') ? 0 : 0.2 / 100; const minAmount = this.safeFloat (market, 'min-order-amt', Math.pow (10, -precision['amount'])); const maxAmount = this.safeFloat (market, 'max-order-amt'); const minCost = this.safeFloat (market, 'min-order-value', 0); const state = this.safeString (market, 'state'); const active = (state === 'online'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'taker': taker, 'maker': maker, 'limits': { 'amount': { 'min': minAmount, 'max': maxAmount, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': minCost, 'max': undefined, }, }, 'info': market, }); } return result; } parseTicker (ticker, market = undefined) { // // fetchTicker // // { // "amount": 26228.672978342216, // "open": 9078.95, // "close": 9146.86, // "high": 9155.41, // "id": 209988544334, // "count": 265846, // "low": 8988.0, // "version": 209988544334, // "ask": [ 9146.87, 0.156134 ], // "vol": 2.3822168242201668E8, // "bid": [ 9146.86, 0.080758 ], // } // // fetchTickers // { // symbol: "bhdht", // open: 2.3938, // high: 2.4151, // low: 2.3323, // close: 2.3909, // amount: 628.992, // vol: 1493.71841095, // count: 2088, // bid: 2.3643, // bidSize: 0.7136, // ask: 2.4061, // askSize: 0.4156 // } // let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger (ticker, 'ts'); let bid = undefined; let bidVolume = undefined; let ask = undefined; let askVolume = undefined; if ('bid' in ticker) { if (Array.isArray (ticker['bid'])) { bid = this.safeFloat (ticker['bid'], 0); bidVolume = this.safeFloat (ticker['bid'], 1); } else { bid = this.safeFloat (ticker, 'bid'); bidVolume = this.safeValue (ticker, 'bidSize'); } } if ('ask' in ticker) { if (Array.isArray (ticker['ask'])) { ask = this.safeFloat (ticker['ask'], 0); askVolume = this.safeFloat (ticker['ask'], 1); } else { ask = this.safeFloat (ticker, 'ask'); askVolume = this.safeValue (ticker, 'askSize'); } } const open = this.safeFloat (ticker, 'open'); const close = this.safeFloat (ticker, 'close'); let change = undefined; let percentage = undefined; let average = undefined; if ((open !== undefined) && (close !== undefined)) { change = close - open; average = this.sum (open, close) / 2; if ((close !== undefined) && (close > 0)) { percentage = (change / open) * 100; } } const baseVolume = this.safeFloat (ticker, 'amount'); const quoteVolume = this.safeFloat (ticker, 'vol'); let vwap = undefined; if (baseVolume !== undefined && quoteVolume !== undefined && baseVolume > 0) { vwap = quoteVolume / baseVolume; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': bid, 'bidVolume': bidVolume, 'ask': ask, 'askVolume': askVolume, 'vwap': vwap, 'open': open, 'close': close, 'last': close, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'type': 'step0', }; const response = await this.marketGetDepth (this.extend (request, params)); // // { // "status": "ok", // "ch": "market.btcusdt.depth.step0", // "ts": 1583474832790, // "tick": { // "bids": [ // [ 9100.290000000000000000, 0.200000000000000000 ], // [ 9099.820000000000000000, 0.200000000000000000 ], // [ 9099.610000000000000000, 0.205000000000000000 ], // ], // "asks": [ // [ 9100.640000000000000000, 0.005904000000000000 ], // [ 9101.010000000000000000, 0.287311000000000000 ], // [ 9101.030000000000000000, 0.012121000000000000 ], // ], // "ts":1583474832008, // "version":104999698780 // } // } // if ('tick' in response) { if (!response['tick']) { throw new ExchangeError (this.id + ' fetchOrderBook() returned empty response: ' + this.json (response)); } const tick = this.safeValue (response, 'tick'); const timestamp = this.safeInteger (tick, 'ts', this.safeInteger (response, 'ts')); const result = this.parseOrderBook (tick, timestamp); result['nonce'] = this.safeInteger (tick, 'version'); return result; } throw new ExchangeError (this.id + ' fetchOrderBook() returned unrecognized response: ' + this.json (response)); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.marketGetDetailMerged (this.extend (request, params)); // // { // "status": "ok", // "ch": "market.btcusdt.detail.merged", // "ts": 1583494336669, // "tick": { // "amount": 26228.672978342216, // "open": 9078.95, // "close": 9146.86, // "high": 9155.41, // "id": 209988544334, // "count": 265846, // "low": 8988.0, // "version": 209988544334, // "ask": [ 9146.87, 0.156134 ], // "vol": 2.3822168242201668E8, // "bid": [ 9146.86, 0.080758 ], // } // } // const ticker = this.parseTicker (response['tick'], market); const timestamp = this.safeValue (response, 'ts'); ticker['timestamp'] = timestamp; ticker['datetime'] = this.iso8601 (timestamp); return ticker; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.marketGetTickers (params); const tickers = this.safeValue (response, 'data'); const timestamp = this.safeInteger (response, 'ts'); const result = {}; for (let i = 0; i < tickers.length; i++) { const marketId = this.safeString (tickers[i], 'symbol'); const market = this.safeValue (this.markets_by_id, marketId); let symbol = marketId; if (market !== undefined) { symbol = market['symbol']; const ticker = this.parseTicker (tickers[i], market); ticker['timestamp'] = timestamp; ticker['datetime'] = this.iso8601 (timestamp); result[symbol] = ticker; } } return this.filterByArray (result, 'symbol', symbols); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "amount": 0.010411000000000000, // "trade-id": 102090736910, // "ts": 1583497692182, // "id": 10500517034273194594947, // "price": 9096.050000000000000000, // "direction": "sell" // } // // fetchMyTrades (private) // let symbol = undefined; if (market === undefined) { const marketId = this.safeString (trade, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger2 (trade, 'ts', 'created-at'); const order = this.safeString (trade, 'order-id'); let side = this.safeString (trade, 'direction'); let type = this.safeString (trade, 'type'); if (type !== undefined) { const typeParts = type.split ('-'); side = typeParts[0]; type = typeParts[1]; } const takerOrMaker = this.safeString (trade, 'role'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat2 (trade, 'filled-amount', 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } let fee = undefined; let feeCost = this.safeFloat (trade, 'filled-fees'); let feeCurrency = undefined; if (market !== undefined) { feeCurrency = (side === 'buy') ? market['base'] : market['quote']; } const filledPoints = this.safeFloat (trade, 'filled-points'); if (filledPoints !== undefined) { if ((feeCost === undefined) || (feeCost === 0.0)) { feeCost = filledPoints; feeCurrency = this.safeCurrencyCode (this.safeString (trade, 'fee-deduct-currency')); } } if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': feeCurrency, }; } const tradeId = this.safeString2 (trade, 'trade-id', 'tradeId'); const id = this.safeString (trade, 'id', tradeId); return { 'id': id, 'info': trade, 'order': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['size'] = limit; // 1-100 orders, default is 100 } if (since !== undefined) { request['start-date'] = this.ymd (since); // maximum query window size is 2 days, query window shift should be within past 120 days } const response = await this.privateGetOrderMatchresults (this.extend (request, params)); const trades = this.parseTrades (response['data'], market, since, limit); return trades; } async fetchTrades (symbol, since = undefined, limit = 1000, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['size'] = limit; } const response = await this.marketGetHistoryTrade (this.extend (request, params)); // // { // "status": "ok", // "ch": "market.btcusdt.trade.detail", // "ts": 1583497692365, // "data": [ // { // "id": 105005170342, // "ts": 1583497692182, // "data": [ // { // "amount": 0.010411000000000000, // "trade-id": 102090736910, // "ts": 1583497692182, // "id": 10500517034273194594947, // "price": 9096.050000000000000000, // "direction": "sell" // } // ] // }, // // ... // ] // } // const data = this.safeValue (response, 'data'); let result = []; for (let i = 0; i < data.length; i++) { const trades = this.safeValue (data[i], 'data', []); for (let j = 0; j < trades.length; j++) { const trade = this.parseTrade (trades[j], market); result.push (trade); } } result = this.sortBy (result, 'timestamp'); return this.filterBySymbolSinceLimit (result, symbol, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ this.safeTimestamp (ohlcv, 'id'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'amount'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 1000, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'period': this.timeframes[timeframe], }; if (limit !== undefined) { request['size'] = limit; } const response = await this.marketGetHistoryKline (this.extend (request, params)); return this.parseOHLCVs (response['data'], market, timeframe, since, limit); } async fetchAccounts (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccountAccounts (params); return response['data']; } async fetchCurrencies (params = {}) { const request = { 'language': this.options['language'], }; const response = await this.publicGetSettingsCurrencys (this.extend (request, params)); const currencies = this.safeValue (response, 'data'); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; // // { name: "ctxc", // 'display-name': "CTXC", // 'withdraw-precision': 8, // 'currency-type': "eth", // 'currency-partition': "pro", // 'support-sites': null, // 'otc-enable': 0, // 'deposit-min-amount': "2", // 'withdraw-min-amount': "4", // 'show-precision': "8", // weight: "2988", // visible: true, // 'deposit-desc': "Please don’t deposit any other digital assets except CTXC t…", // 'withdraw-desc': "Minimum withdrawal amount: 4 CTXC. !>_ 0)) { average = cost / filled; } } const feeCost = this.safeFloat2 (order, 'filled-fees', 'field-fees'); // typo in their API, filled fees let fee = undefined; if (feeCost !== undefined) { let feeCurrency = undefined; if (market !== undefined) { feeCurrency = (side === 'sell') ? market['quote'] : market['base']; } fee = { 'cost': feeCost, 'currency': feeCurrency, }; } return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'average': average, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); await this.loadAccounts (); const market = this.market (symbol); const request = { 'account-id': this.accounts[0]['id'], 'symbol': market['id'], 'type': side + '-' + type, }; if ((type === 'market') && (side === 'buy')) { if (this.options['createMarketBuyOrderRequiresPrice']) { if (price === undefined) { throw new InvalidOrder (this.id + " market buy order requires price argument to calculate cost (total amount of quote currency to spend for buying, amount * price). To switch off this warning exception and specify cost in the amount argument, set .options['createMarketBuyOrderRequiresPrice'] = false. Make sure you know what you're doing."); } else { // despite that cost = amount * price is in quote currency and should have quote precision // the exchange API requires the cost supplied in 'amount' to be of base precision // more about it here: https://github.com/ccxt/ccxt/pull/4395 // we use priceToPrecision instead of amountToPrecision here // because in this case the amount is in the quote currency request['amount'] = this.costToPrecision (symbol, parseFloat (amount) * parseFloat (price)); } } else { request['amount'] = this.costToPrecision (symbol, amount); } } else { request['amount'] = this.amountToPrecision (symbol, amount); } if (type === 'limit' || type === 'ioc' || type === 'limit-maker') { request['price'] = this.priceToPrecision (symbol, price); } const method = this.options['createOrderMethod']; const response = await this[method] (this.extend (request, params)); const timestamp = this.milliseconds (); const id = this.safeString (response, 'data'); return { 'info': response, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'filled': undefined, 'remaining': undefined, 'cost': undefined, 'trades': undefined, 'fee': undefined, 'clientOrderId': undefined, 'average': undefined, }; } async cancelOrder (id, symbol = undefined, params = {}) { const response = await this.privatePostOrderOrdersIdSubmitcancel ({ 'id': id }); // // let response = { // 'status': 'ok', // 'data': '10138899000', // }; // return this.extend (this.parseOrder (response), { 'id': id, 'status': 'canceled', }); } currencyToPrecision (currency, fee) { return this.decimalToPrecision (fee, 0, this.currencies[currency]['precision']); } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; const rate = market[takerOrMaker]; let cost = amount * rate; let key = 'quote'; if (side === 'sell') { cost *= price; } else { key = 'base'; } return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': parseFloat (this.currencyToPrecision (market[key], cost)), }; } parseDepositAddress (depositAddress, currency = undefined) { // // { // currency: "eth", // address: "0xf7292eb9ba7bc50358e27f0e025a4d225a64127b", // addressTag: "", // chain: "eth" // } // const address = this.safeString (depositAddress, 'address'); const tag = this.safeString (depositAddress, 'addressTag'); const currencyId = this.safeString (depositAddress, 'currency'); const code = this.safeCurrencyCode (currencyId); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': depositAddress, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.v2PrivateGetAccountDepositAddress (this.extend (request, params)); // // { // code: 200, // data: [ // { // currency: "eth", // address: "0xf7292eb9ba7bc50358e27f0e025a4d225a64127b", // addressTag: "", // chain: "eth" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseDepositAddress (this.safeValue (data, 0, {}), currency); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { if (limit === undefined || limit > 100) { limit = 100; } await this.loadMarkets (); let currency = undefined; if (code !== undefined) { currency = this.currency (code); } const request = { 'type': 'deposit', 'from': 0, // From 'id' ... if you want to get results after a particular transaction id, pass the id in params.from }; if (currency !== undefined) { request['currency'] = currency['id']; } if (limit !== undefined) { request['size'] = limit; // max 100 } const response = await this.privateGetQueryDepositWithdraw (this.extend (request, params)); // return response return this.parseTransactions (response['data'], currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { if (limit === undefined || limit > 100) { limit = 100; } await this.loadMarkets (); let currency = undefined; if (code !== undefined) { currency = this.currency (code); } const request = { 'type': 'withdraw', 'from': 0, // From 'id' ... if you want to get results after a particular transaction id, pass the id in params.from }; if (currency !== undefined) { request['currency'] = currency['id']; } if (limit !== undefined) { request['size'] = limit; // max 100 } const response = await this.privateGetQueryDepositWithdraw (this.extend (request, params)); // return response return this.parseTransactions (response['data'], currency, since, limit); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // 'id': 8211029, // 'type': 'deposit', // 'currency': 'eth', // 'chain': 'eth', // 'tx-hash': 'bd315....', // 'amount': 0.81162421, // 'address': '4b8b....', // 'address-tag': '', // 'fee': 0, // 'state': 'safe', // 'created-at': 1542180380965, // 'updated-at': 1542180788077 // } // // fetchWithdrawals // // { // 'id': 6908275, // 'type': 'withdraw', // 'currency': 'btc', // 'chain': 'btc', // 'tx-hash': 'c1a1a....', // 'amount': 0.80257005, // 'address': '1QR....', // 'address-tag': '', // 'fee': 0.0005, // 'state': 'confirmed', // 'created-at': 1552107295685, // 'updated-at': 1552108032859 // } // const timestamp = this.safeInteger (transaction, 'created-at'); const updated = this.safeInteger (transaction, 'updated-at'); const code = this.safeCurrencyCode (this.safeString (transaction, 'currency')); let type = this.safeString (transaction, 'type'); if (type === 'withdraw') { type = 'withdrawal'; } const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); const tag = this.safeString (transaction, 'address-tag'); let feeCost = this.safeFloat (transaction, 'fee'); if (feeCost !== undefined) { feeCost = Math.abs (feeCost); } return { 'info': transaction, 'id': this.safeString (transaction, 'id'), 'txid': this.safeString (transaction, 'tx-hash'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': this.safeString (transaction, 'address'), 'tag': tag, 'type': type, 'amount': this.safeFloat (transaction, 'amount'), 'currency': code, 'status': status, 'updated': updated, 'fee': { 'currency': code, 'cost': feeCost, 'rate': undefined, }, }; } parseTransactionStatus (status) { const statuses = { // deposit statuses 'unknown': 'failed', 'confirming': 'pending', 'confirmed': 'ok', 'safe': 'ok', 'orphan': 'failed', // withdrawal statuses 'submitted': 'pending', 'canceled': 'canceled', 'reexamine': 'pending', 'reject': 'failed', 'pass': 'pending', 'wallet-reject': 'failed', // 'confirmed': 'ok', // present in deposit statuses 'confirm-error': 'failed', 'repealed': 'failed', 'wallet-transfer': 'pending', 'pre-transfer': 'pending', }; return this.safeString (statuses, status, status); } async withdraw (code, amount, address, tag = undefined, params = {}) { await this.loadMarkets (); this.checkAddress (address); const currency = this.currency (code); const request = { 'address': address, // only supports existing addresses in your withdraw address list 'amount': amount, 'currency': currency['id'].toLowerCase (), }; if (tag !== undefined) { request['addr-tag'] = tag; // only for XRP? } const response = await this.privatePostDwWithdrawApiCreate (this.extend (request, params)); const id = this.safeString (response, 'data'); return { 'info': response, 'id': id, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = '/'; if (api === 'market') { url += api; } else if ((api === 'public') || (api === 'private')) { url += this.version; } else if ((api === 'v2Public') || (api === 'v2Private')) { url += 'v2'; } url += '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'private' || api === 'v2Private') { this.checkRequiredCredentials (); const timestamp = this.ymdhms (this.milliseconds (), 'T'); let request = { 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AccessKeyId': this.apiKey, 'Timestamp': timestamp, }; if (method !== 'POST') { request = this.extend (request, query); } request = this.keysort (request); let auth = this.urlencode (request); // unfortunately, PHP demands double quotes for the escaped newline symbol // eslint-disable-next-line quotes const payload = [ method, this.hostname, url, auth ].join ("\n"); const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256', 'base64'); auth += '&' + this.urlencode ({ 'Signature': signature }); url += '?' + auth; if (method === 'POST') { body = this.json (query); headers = { 'Content-Type': 'application/json', }; } else { headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; } } else { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } url = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname, }) + url; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('status' in response) { // // {"status":"error","err-code":"order-limitorder-amount-min-error","err-msg":"limit order amount error, min: `0.001`","data":null} // const status = this.safeString (response, 'status'); if (status === 'error') { const code = this.safeString (response, 'err-code'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); const message = this.safeString (response, 'err-msg'); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9}],93:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const huobipro = require ('./huobipro.js'); // --------------------------------------------------------------------------- module.exports = class huobiru extends huobipro { describe () { return this.deepExtend (super.describe (), { 'id': 'huobiru', 'name': 'Huobi Russia', 'countries': [ 'RU' ], 'hostname': 'www.huobi.com.ru', 'pro': true, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/52978816-e8552e00-33e3-11e9-98ed-845acfece834.jpg', 'api': { 'market': 'https://{hostname}/api', 'public': 'https://{hostname}/api', 'private': 'https://{hostname}/api', 'zendesk': 'https://huobiglobal.zendesk.com/hc/en-us/articles', }, 'www': 'https://www.huobi.com.ru/ru-ru', 'referral': 'https://www.huobi.com.ru/invite?invite_code=esc74', 'doc': 'https://github.com/cloudapidoc/API_Docs_en', 'fees': 'https://www.huobi.com.ru/ru-ru/about/fee/', }, }); } }; },{"./huobipro.js":92}],94:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class ice3x extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'ice3x', 'name': 'ICE3X', 'countries': [ 'ZA' ], // South Africa 'rateLimit': 1000, 'version': 'v1', 'has': { 'fetchCurrencies': true, 'fetchTickers': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchMyTrades': true, 'fetchDepositAddress': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/38012176-11616c32-3269-11e8-9f05-e65cf885bb15.jpg', 'api': 'https://ice3x.com/api', 'www': [ 'https://ice3x.com', 'https://ice3x.co.za', ], 'doc': 'https://ice3x.co.za/ice-cubed-bitcoin-exchange-api-documentation-1-june-2017', 'fees': [ 'https://help.ice3.com/support/solutions/articles/11000033293-trading-fees', 'https://help.ice3.com/support/solutions/articles/11000033288-fees-explained', 'https://help.ice3.com/support/solutions/articles/11000008131-what-are-your-fiat-deposit-and-withdrawal-fees-', 'https://help.ice3.com/support/solutions/articles/11000033289-deposit-fees', ], 'referral': 'https://ice3x.com?ref=14341802', }, 'api': { 'public': { 'get': [ 'currency/list', 'currency/info', 'pair/list', 'pair/info', 'stats/marketdepthfull', 'stats/marketdepthbtcav', 'stats/marketdepth', 'orderbook/info', 'trade/list', 'trade/info', ], }, 'private': { 'post': [ 'balance/list', 'balance/info', 'order/new', 'order/cancel', 'order/list', 'order/info', 'trade/list', 'trade/info', 'transaction/list', 'transaction/info', 'invoice/list', 'invoice/info', 'invoice/pdf', ], }, }, 'fees': { 'trading': { 'maker': 0.005, 'taker': 0.005, }, }, 'precision': { 'amount': 8, 'price': 8, }, }); } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencyList (params); const currencies = response['response']['entities']; const precision = this.precision['amount']; const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'currency_id'); const currencyId = this.safeString (currency, 'iso'); const code = this.safeCurrencyCode (currencyId); result[code] = { 'id': id, 'code': code, 'name': currency['name'], 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': undefined, 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': currency, 'fee': undefined, }; } return result; } async fetchMarkets (params = {}) { if (this.currencies_by_id === undefined) { this.currencies = await this.fetchCurrencies (); this.currencies_by_id = this.indexBy (this.currencies, 'id'); } const response = await this.publicGetPairList (params); const markets = this.safeValue (response['response'], 'entities'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'pair_id'); const baseId = this.safeString (market, 'currency_id_from'); const quoteId = this.safeString (market, 'currency_id_to'); const baseCurrency = this.currencies_by_id[baseId]; const quoteCurrency = this.currencies_by_id[quoteId]; const base = baseCurrency['code']; const quote = quoteCurrency['code']; const symbol = base + '/' + quote; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': undefined, 'info': market, 'precision': this.precision, 'limits': this.limits, }); } return result; } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); const symbol = market['symbol']; const last = this.safeFloat (ticker, 'last_price'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'max'), 'low': this.safeFloat (ticker, 'min'), 'bid': this.safeFloat (ticker, 'max_bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'min_ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker, 'avg'), 'baseVolume': undefined, 'quoteVolume': this.safeFloat (ticker, 'vol'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair_id': market['id'], }; const response = await this.publicGetStatsMarketdepthfull (this.extend (request, params)); const ticker = this.safeValue (response['response'], 'entity'); return this.parseTicker (ticker, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetStatsMarketdepthfull (params); const tickers = this.safeValue (response['response'], 'entities'); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = tickers[i]; const marketId = this.safeString (ticker, 'pair_id'); const market = this.safeValue (this.marketsById, marketId); if (market !== undefined) { const symbol = market['symbol']; result[symbol] = this.parseTicker (ticker, market); } } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair_id': this.marketId (symbol), }; if (limit !== undefined) { const type = this.safeString (params, 'type'); if ((type !== 'ask') && (type !== 'bid')) { // eslint-disable-next-line quotes throw new ArgumentsRequired (this.id + " fetchOrderBook requires an exchange-specific extra 'type' param ('bid' or 'ask') when used with a limit"); } else { request['items_per_page'] = limit; } } const response = await this.publicGetOrderbookInfo (this.extend (request, params)); const orderbook = this.safeValue (response['response'], 'entities'); return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'amount'); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'created'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'volume'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': market['quote'], }; } const type = 'limit'; const side = this.safeString (trade, 'type'); const id = this.safeString (trade, 'trade_id'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair_id': market['id'], }; const response = await this.publicGetTradeList (this.extend (request, params)); const trades = this.safeValue (response['response'], 'entities'); return this.parseTrades (trades, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostBalanceList (params); const result = { 'info': response }; const balances = this.safeValue (response['response'], 'entities', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; // currency ids are numeric strings const currencyId = this.safeString (balance, 'currency_id'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balance, 'balance'); result[code] = account; } return this.parseBalance (result); } parseOrder (order, market = undefined) { const pairId = this.safeInteger (order, 'pair_id'); let symbol = undefined; if (pairId && !market && (pairId in this.marketsById)) { market = this.marketsById[pairId]; symbol = market['symbol']; } const timestamp = this.safeTimestamp (order, 'created'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'volume'); let status = this.safeInteger (order, 'active'); let remaining = this.safeFloat (order, 'remaining'); let filled = undefined; if (status === 1) { status = 'open'; } else { status = 'closed'; remaining = 0; filled = amount; } let fee = undefined; const feeCost = this.safeFloat (order, 'fee'); if (feeCost !== undefined) { fee = { 'cost': feeCost, }; if (market !== undefined) { fee['currency'] = market['quote']; } } return { 'id': this.safeString (order, 'order_id'), 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': 'limit', 'side': this.safeStrin (order, 'type'), 'price': price, 'cost': undefined, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': fee, 'info': order, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair_id': market['id'], 'type': side, 'amount': amount, 'price': price, }; const response = await this.privatePostOrderNew (this.extend (request, params)); const order = this.parseOrder ({ 'order_id': response['response']['entity']['order_id'], 'created': this.seconds (), 'active': 1, 'type': side, 'price': price, 'volume': amount, 'remaining': amount, 'info': response, }, market); const id = order['id']; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'order_id': id, }; return await this.privatePostOrderCancel (this.extend (request, params)); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order _id': id, }; const response = await this.privatePostOrderInfo (this.extend (request, params)); const order = this.safeValue (response['response'], 'entity'); return this.parseOrder (order); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.privatePostOrderList (params); const orders = this.safeValue (response['response'], 'entities'); return this.parseOrders (orders, undefined, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair_id': market['id'], }; if (limit !== undefined) { request['items_per_page'] = limit; } if (since !== undefined) { request['date_from'] = parseInt (since / 1000); } const response = await this.privatePostTradeList (this.extend (request, params)); const trades = this.safeValue (response['response'], 'entities'); return this.parseTrades (trades, market, since, limit); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency_id': currency['id'], }; const response = await this.privatePostBalanceInfo (this.extend (request, params)); const balance = this.safeValue (response['response'], 'entity'); const address = this.safeString (balance, 'address'); const status = address ? 'ok' : 'none'; return { 'currency': code, 'address': address, 'tag': undefined, 'status': status, 'info': response, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); body = this.urlencode (this.extend ({ 'nonce': this.nonce (), }, params)); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); const errors = this.safeValue (response, 'errors'); const data = this.safeValue (response, 'response'); if (errors || !data) { const authErrorKeys = [ 'Key', 'user_id', 'Sign' ]; for (let i = 0; i < authErrorKeys.length; i++) { const errorKey = authErrorKeys[i]; const errorMessage = this.safeString (errors, errorKey); if (!errorMessage) { continue; } if (errorKey === 'user_id' && errorMessage.indexOf ('authorization') < 0) { continue; } throw new AuthenticationError (errorMessage); } throw new ExchangeError (this.json (errors)); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],95:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class idex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'idex', 'name': 'IDEX', 'countries': [ 'US' ], 'rateLimit': 1500, 'certified': true, 'requiresWeb3': true, 'has': { 'fetchOrderBook': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchMarkets': true, 'fetchBalance': true, 'createOrder': true, 'cancelOrder': true, 'fetchOpenOrders': true, 'fetchTransactions': true, 'fetchTrades': true, 'fetchMyTrades': true, 'withdraw': true, 'fetchOHLCV': false, }, 'timeframes': { '1m': 'M1', '3m': 'M3', '5m': 'M5', '15m': 'M15', '30m': 'M30', // default '1h': 'H1', '4h': 'H4', '1d': 'D1', '1w': 'D7', '1M': '1M', }, 'urls': { 'test': 'https://api.idex.market', 'logo': 'https://user-images.githubusercontent.com/1294454/63693236-3415e380-c81c-11e9-8600-ba1634f1407d.jpg', 'api': 'https://api.idex.market', 'www': 'https://idex.market', 'doc': [ 'https://docs.idex.market/', ], }, 'api': { 'public': { 'post': [ 'returnTicker', 'returnCurrenciesWithPairs', // undocumented 'returnCurrencies', 'return24Volume', 'returnBalances', 'returnCompleteBalances', // shows amount in orders as well as total 'returnDepositsWithdrawals', 'returnOpenOrders', 'returnOrderBook', 'returnOrderStatus', 'returnOrderTrades', 'returnTradeHistory', 'returnTradeHistoryMeta', // not documented 'returnContractAddress', 'returnNextNonce', ], }, 'private': { 'post': [ 'order', 'cancel', 'trade', 'withdraw', ], }, }, 'options': { 'contractAddress': undefined, // 0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208 'orderNonce': undefined, }, 'exceptions': { 'Invalid order signature. Please try again.': AuthenticationError, 'You have insufficient funds to match this order. If you believe this is a mistake please refresh and try again.': InsufficientFunds, 'Order no longer available.': InvalidOrder, }, 'requiredCredentials': { 'walletAddress': true, 'privateKey': true, 'apiKey': false, 'secret': false, }, 'commonCurrencies': { 'FT': 'Fabric Token', 'MT': 'Monarch', 'ONE': 'Menlo One', 'PLA': 'PlayChip', 'WAX': 'WAXP', }, }); } async fetchMarkets (params = {}) { // idex does not have an endpoint for markets // instead we generate the markets from the endpoint for currencies const request = { 'includeDelisted': true, }; const markets = await this.publicPostReturnCurrenciesWithPairs (this.extend (request, params)); const currenciesById = {}; const currencies = markets['tokens']; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; currenciesById[currency['symbol']] = currency; } const result = []; const limits = { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }; const quotes = markets['pairs']; const keys = Object.keys (quotes); for (let i = 0; i < keys.length; i++) { const quoteId = keys[i]; const bases = quotes[quoteId]; const quote = this.safeCurrencyCode (quoteId); const quoteCurrency = currenciesById[quoteId]; for (let j = 0; j < bases.length; j++) { const baseId = bases[j]; const id = quoteId + '_' + baseId; const base = this.safeCurrencyCode (baseId); const symbol = base + '/' + quote; const baseCurrency = currenciesById[baseId]; const baseAddress = baseCurrency['address']; const quoteAddress = quoteCurrency['address']; const precision = { 'price': this.safeInteger (quoteCurrency, 'decimals'), 'amount': this.safeInteger (baseCurrency, 'decimals'), }; result.push ({ 'symbol': symbol, 'precision': precision, 'base': base, 'quote': quote, 'baseId': baseAddress, 'quoteId': quoteAddress, 'limits': limits, 'id': id, 'info': baseCurrency, 'tierBased': false, }); } } return result; } parseTicker (ticker, market = undefined) { // // { // last: '0.0016550916', // high: 'N/A', // low: 'N/A', // lowestAsk: '0.0016743368', // highestBid: '0.001163726270773897', // percentChange: '0', // baseVolume: '0', // quoteVolume: '0' // } // let symbol = undefined; if (market) { symbol = market['symbol']; } const baseVolume = this.safeFloat (ticker, 'baseVolume'); const quoteVolume = this.safeFloat (ticker, 'quoteVolume'); const last = this.safeFloat (ticker, 'last'); const percentage = this.safeFloat (ticker, 'percentChange'); return { 'symbol': symbol, 'timestamp': undefined, 'datetime': undefined, 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'highestBid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'lowestAsk'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': percentage, 'average': undefined, 'baseVolume': quoteVolume, 'quoteVolume': baseVolume, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicPostReturnTicker (params); // { ETH_BOUNCY: // { last: '0.000000004000088005', // high: 'N/A', // low: 'N/A', // lowestAsk: '0.00000000599885995', // highestBid: '0.000000001400500103', // percentChange: '0', // baseVolume: '0', // quoteVolume: '0' }, // ETH_NBAI: // { last: '0.0000032', // high: 'N/A', // low: 'N/A', // lowestAsk: '0.000004000199999502', // highestBid: '0.0000016002', // percentChange: '0', // baseVolume: '0', // quoteVolume: '0' }, } const ids = Object.keys (response); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; let symbol = undefined; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } else { const [ quoteId, baseId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; market = { 'symbol': symbol }; } const ticker = response[id]; result[symbol] = this.parseTicker (ticker, market); } return this.filterByArray (result, 'symbol', symbols); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicPostReturnTicker (this.extend (request, params)); // { last: '0.0016550916', // high: 'N/A', // low: 'N/A', // lowestAsk: '0.0016743368', // highestBid: '0.001163726270773897', // percentChange: '0', // baseVolume: '0', // quoteVolume: '0' } return this.parseTicker (response, market); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const id = market['quote'] + '_' + market['base']; const request = { 'market': id, 'count': 100, // the default will only return one trade }; if (limit !== undefined) { request['count'] = limit; } const response = await this.publicPostReturnOrderBook (this.extend (request, params)); // // { // "asks": [ // { // "price": "0.001675282799999", // "amount": "206.163978911921061732", // "total": "0.345382967850497906", // "orderHash": "0xfdf12c124a6a7fa4a8e1866b324da888c8e1b3ad209f5050d3a23df3397a5cb7", // "params": { // "tokenBuy": "0x0000000000000000000000000000000000000000", // "buySymbol": "ETH", // "buyPrecision": 18, // "amountBuy": "345382967850497906", // "tokenSell": "0xb98d4c97425d9908e66e53a6fdf673acca0be986", // "sellSymbol": "ABT", // "sellPrecision": 18, // "amountSell": "206163978911921061732", // "expires": 10000, // "nonce": 13489307413, // "user": "0x9e8ef79316a4a79bbf55a5f9c16b3e068fff65c6" // } // } // ], // "bids": [ // { // "price": "0.001161865193232242", // "amount": "854.393661648355", // "total": "0.992690256787469029", // "orderHash": "0x2f2baaf982085e4096f9e23e376214885fa74b2939497968e92222716fc2c86d", // "params": { // "tokenBuy": "0xb98d4c97425d9908e66e53a6fdf673acca0be986", // "buySymbol": "ABT", // "buyPrecision": 18, // "amountBuy": "854393661648355000000", // "tokenSell": "0x0000000000000000000000000000000000000000", // "sellSymbol": "ETH", // "sellPrecision": 18, // "amountSell": "992690256787469029", // "expires": 10000, // "nonce": 18155189676, // "user": "0xb631284dd7b74a846af5b37766ceb1f85d53eca4" // } // } // ] // } // return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); } parseBidAsk (bidAsk, priceKey = 0, amountKey = 1) { const price = this.safeFloat (bidAsk, priceKey); const amount = this.safeFloat (bidAsk, amountKey); const info = bidAsk; return [price, amount, info]; } async fetchBalance (params = {}) { const request = { 'address': this.walletAddress, }; const response = await this.publicPostReturnCompleteBalances (this.extend (request, params)); // // { // ETH: { available: '0.0167', onOrders: '0.1533' } // } // const result = { 'info': response, }; const keys = Object.keys (response); for (let i = 0; i < keys.length; i++) { const currency = keys[i]; const balance = response[currency]; const code = this.safeCurrencyCode (currency); result[code] = { 'free': this.safeFloat (balance, 'available'), 'used': this.safeFloat (balance, 'onOrders'), }; } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { this.checkRequiredDependencies (); await this.loadMarkets (); const market = this.market (symbol); if (type === 'limit') { const expires = 100000; const contractAddress = await this.getContractAddress (); let tokenBuy = undefined; let tokenSell = undefined; let amountBuy = undefined; let amountSell = undefined; const quoteAmount = parseFloat (price) * parseFloat (amount); if (side === 'buy') { tokenBuy = market['baseId']; tokenSell = market['quoteId']; amountBuy = this.toWei (amount, market['precision']['amount']); amountSell = this.toWei (quoteAmount, 18); } else { tokenBuy = market['quoteId']; tokenSell = market['baseId']; amountBuy = this.toWei (quoteAmount, 18); amountSell = this.toWei (amount, market['precision']['amount']); } const nonce = await this.getNonce (); const orderToHash = { 'contractAddress': contractAddress, 'tokenBuy': tokenBuy, 'amountBuy': amountBuy, 'tokenSell': tokenSell, 'amountSell': amountSell, 'expires': expires, 'nonce': nonce, 'address': this.walletAddress, }; const orderHash = this.getIdexCreateOrderHash (orderToHash); const signature = this.signMessage (orderHash, this.privateKey); const request = { 'tokenBuy': tokenBuy, 'amountBuy': amountBuy, 'tokenSell': tokenSell, 'amountSell': amountSell, 'address': this.walletAddress, 'nonce': nonce, 'expires': expires, }; const response = await this.privatePostOrder (this.extend (request, signature)); // this.extend (request, params) will cause invalid signature // { orderNumber: 1562323021, // orderHash: // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', // timestamp: 1564041428, // price: '0.00073', // amount: '210', // total: '0.1533', // type: 'buy', // params: // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', // buyPrecision: 18, // amountBuy: '210000000000000000000', // tokenSell: '0x0000000000000000000000000000000000000000', // sellPrecision: 18, // amountSell: '153300000000000000', // expires: 100000, // nonce: 1, // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' } } return this.parseOrder (response, market); } else if (type === 'market') { if (!('orderHash' in params)) { throw new ArgumentsRequired (this.id + ' market order requires an order structure such as that in fetchOrderBook()[\'bids\'][0][2], fetchOrder()[\'info\'], or fetchOpenOrders()[0][\'info\']'); } // { price: '0.000132247803328924', // amount: '19980', // total: '2.6423111105119', // orderHash: // '0x5fb3452b3d13fc013585b51c91c43a0fbe4298c211243763c49437848c274749', // params: // { tokenBuy: '0x0000000000000000000000000000000000000000', // buySymbol: 'ETH', // buyPrecision: 18, // amountBuy: '2642311110511900000', // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', // sellSymbol: 'IDEX', // sellPrecision: 18, // amountSell: '19980000000000000000000', // expires: 10000, // nonce: 1564656561510, // user: '0xc3f8304270e49b8e8197bfcfd8567b83d9e4479b' } } const orderToSign = { 'orderHash': params['orderHash'], 'amount': params['params']['amountBuy'], 'address': params['params']['user'], 'nonce': params['params']['nonce'], }; const orderHash = this.getIdexMarketOrderHash (orderToSign); const signature = this.signMessage (orderHash, this.privateKey); const signedOrder = this.extend (orderToSign, signature); signedOrder['address'] = this.walletAddress; signedOrder['nonce'] = await this.getNonce (); // [ { // "amount": "0.07", // "date": "2017-10-13 16:25:36", // "total": "0.49", // "market": "ETH_DVIP", // "type": "buy", // "price": "7", // "orderHash": "0xcfe4018c59e50e0e1964c979e6213ce5eb8c751cbc98a44251eb48a0985adc52", // "uuid": "250d51a0-b033-11e7-9984-a9ab79bb8f35" // } ] const response = await this.privatePostTrade (signedOrder); return this.parseOrders (response, market); } } async getNonce () { if (this.options['orderNonce'] === undefined) { const response = await this.publicPostReturnNextNonce ({ 'address': this.walletAddress, }); return this.safeInteger (response, 'nonce'); } else { const result = this.options['orderNonce']; this.options['orderNonce'] = this.sum (this.options['orderNonce'], 1); return result; } } async getContractAddress () { if (this.options['contractAddress'] !== undefined) { return this.options['contractAddress']; } const response = await this.publicPostReturnContractAddress (); this.options['contractAddress'] = this.safeString (response, 'address'); return this.options['contractAddress']; } async cancelOrder (orderId, symbol = undefined, params = {}) { const nonce = await this.getNonce (); const orderToHash = { 'orderHash': orderId, 'nonce': nonce, }; const orderHash = this.getIdexCancelOrderHash (orderToHash); const signature = this.signMessage (orderHash, this.privateKey); const request = { 'orderHash': orderId, 'address': this.walletAddress, 'nonce': nonce, }; const response = await this.privatePostCancel (this.extend (request, signature)); // { success: 1 } if ('success' in response) { return { 'info': response, }; } else { throw new ExchangeError (this.id + ' cancel order failed ' + this.json (response)); } } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'address': this.walletAddress, }; if (since !== undefined) { request['start'] = parseInt (Math.floor (since / 1000)); } const response = await this.publicPostReturnDepositsWithdrawals (this.extend (request, params)); // { deposits: // [ { currency: 'ETH', // amount: '0.05', // timestamp: 1563953513, // transactionHash: // '0xd6eefd81c7efc9beeb35b924d6db3c93a78bf7eac082ba87e107ad4e94bccdcf', // depositNumber: 1586430 }, // { currency: 'ETH', // amount: '0.12', // timestamp: 1564040359, // transactionHash: // '0x2ecbb3ab72b6f79fc7a9058c39dce28f913152748c1507d13ab1759e965da3ca', // depositNumber: 1587341 } ], // withdrawals: // [ { currency: 'ETH', // amount: '0.149', // timestamp: 1564060001, // transactionHash: // '0xab555fc301779dd92fd41ccd143b1d72776ae7b5acfc59ca44a1d376f68fda15', // withdrawalNumber: 1444070, // status: 'COMPLETE' } ] } const deposits = this.parseTransactions (response['deposits'], currency, since, limit); const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); return this.arrayConcat (deposits, withdrawals); } parseTransaction (item, currency = undefined) { // { currency: 'ETH', // amount: '0.05', // timestamp: 1563953513, // transactionHash: // '0xd6eefd81c7efc9beeb35b924d6db3c93a78bf7eac082ba87e107ad4e94bccdcf', // depositNumber: 1586430 } const amount = this.safeFloat (item, 'amount'); const timestamp = this.safeTimestamp (item, 'timestamp'); const txhash = this.safeString (item, 'transactionHash'); let id = undefined; let type = undefined; let status = undefined; let addressFrom = undefined; let addressTo = undefined; if ('depositNumber' in item) { id = this.safeString (item, 'depositNumber'); type = 'deposit'; addressFrom = this.walletAddress; addressTo = this.options['contractAddress']; } else if ('withdrawalNumber' in item) { id = this.safeString (item, 'withdrawalNumber'); type = 'withdrawal'; status = this.parseTransactionStatus (this.safeString (item, 'status')); addressFrom = this.options['contractAddress']; addressTo = this.walletAddress; } const code = this.safeCurrencyCode (this.safeString (item, 'currency')); return { 'info': item, 'id': id, 'txid': txhash, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'currency': code, 'amount': amount, 'status': status, 'type': type, 'updated': undefined, 'comment': undefined, 'addressFrom': addressFrom, 'tagFrom': undefined, 'addressTo': addressTo, 'tagTo': undefined, 'fee': { 'currency': code, 'cost': undefined, 'rate': undefined, }, }; } parseTransactionStatus (status) { const statuses = { 'COMPLETE': 'ok', }; return this.safeString (statuses, status); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (this.walletAddress === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a walletAddress'); } await this.loadMarkets (); const request = { 'address': this.walletAddress, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const response = await this.publicPostReturnOpenOrders (this.extend (request, params)); // [ { timestamp: 1564041428, // orderHash: // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', // orderNumber: 1562323021, // market: 'ETH_LIT', // type: 'buy', // params: // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', // buySymbol: 'LIT', // buyPrecision: 18, // amountBuy: '210000000000000000000', // tokenSell: '0x0000000000000000000000000000000000000000', // sellSymbol: 'ETH', // sellPrecision: 18, // amountSell: '153300000000000000', // expires: 100000, // nonce: 1, // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, // price: '0.00073', // amount: '210', // status: 'open', // total: '0.1533' } ] return this.parseOrders (response, market, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'orderHash': id, }; const response = await this.publicPostReturnOrderStatus (this.extend (request, params)); // { filled: '0', // initialAmount: '210', // timestamp: 1564041428, // orderHash: // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', // orderNumber: 1562323021, // market: 'ETH_LIT', // type: 'buy', // params: // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', // buySymbol: 'LIT', // buyPrecision: 18, // amountBuy: '210000000000000000000', // tokenSell: '0x0000000000000000000000000000000000000000', // sellSymbol: 'ETH', // sellPrecision: 18, // amountSell: '153300000000000000', // expires: 100000, // nonce: 1, // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, // price: '0.00073', // amount: '210', // status: 'open', // total: '0.1533' } return this.parseOrder (response, market); } parseOrder (order, market = undefined) { // { filled: '0', // initialAmount: '210', // timestamp: 1564041428, // orderHash: // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', // orderNumber: 1562323021, // market: 'ETH_LIT', // type: 'buy', // params: // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', // buySymbol: 'LIT', // buyPrecision: 18, // amountBuy: '210000000000000000000', // tokenSell: '0x0000000000000000000000000000000000000000', // sellSymbol: 'ETH', // sellPrecision: 18, // amountSell: '153300000000000000', // expires: 100000, // nonce: 1, // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, // price: '0.00073', // amount: '210', // status: 'open', // total: '0.1533' } const timestamp = this.safeTimestamp (order, 'timestamp'); const side = this.safeString (order, 'type'); let symbol = undefined; let amount = undefined; let remaining = undefined; if ('initialAmount' in order) { amount = this.safeFloat (order, 'initialAmount'); remaining = this.safeFloat (order, 'amount'); } else { amount = this.safeFloat (order, 'amount'); } const filled = this.safeFloat (order, 'filled'); const price = this.safeFloat (order, 'price'); let cost = this.safeFloat (order, 'total'); if ((cost !== undefined) && (filled !== undefined) && !cost) { cost = filled * price; } if ('market' in order) { const marketId = order['market']; symbol = this.markets_by_id[marketId]['symbol']; } else if ((side !== undefined) && ('params' in order)) { const params = order['params']; const buy = this.safeCurrencyCode (this.safeString (params, 'tokenBuy')); const sell = this.safeCurrencyCode (this.safeString (params, 'tokenSell')); if (buy !== undefined && sell !== undefined) { symbol = (side === 'buy') ? (buy + '/' + sell) : (sell + '/' + buy); } } if (symbol === undefined && market !== undefined) { symbol = market['symbol']; } const id = this.safeString (order, 'orderHash'); const status = this.parseOrderStatus (this.safeString (order, 'status')); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'side': side, 'amount': amount, 'price': price, 'type': 'limit', 'filled': filled, 'remaining': remaining, 'cost': cost, 'status': status, 'lastTradeTimestamp': undefined, 'average': undefined, 'trades': undefined, 'fee': undefined, }; } parseOrderStatus (status) { const statuses = { 'open': 'open', }; return this.safeString (statuses, status, status); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (this.walletAddress === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a walletAddress'); } await this.loadMarkets (); const request = { 'address': this.walletAddress, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } if (limit !== undefined) { request['start'] = parseInt (Math.floor (limit)); } const response = await this.publicPostReturnTradeHistory (this.extend (request, params)); // { ETH_IDEX: // [ { type: 'buy', // date: '2019-07-25 11:24:41', // amount: '347.833140025692348611', // total: '0.050998794333719943', // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', // tid: 4320867, // timestamp: 1564053881, // price: '0.000146618560640751', // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', // orderHash: // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', // transactionHash: // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', // tokenBuy: '0x0000000000000000000000000000000000000000', // buyerFee: '0.695666280051384697', // gasFee: '28.986780264563232993', // sellerFee: '0.00005099879433372', // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', // usdValue: '11.336926687304238214' } ] } // // if a symbol is specified in the request: // // [ { type: 'buy', // date: '2019-07-25 11:24:41', // amount: '347.833140025692348611', // total: '0.050998794333719943', // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', // tid: 4320867, // timestamp: 1564053881, // price: '0.000146618560640751', // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', // orderHash: // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', // transactionHash: // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', // tokenBuy: '0x0000000000000000000000000000000000000000', // buyerFee: '0.695666280051384697', // gasFee: '28.986780264563232993', // sellerFee: '0.00005099879433372', // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', // usdValue: '11.336926687304238214' } ] if (Array.isArray (response)) { return this.parseTrades (response, market, since, limit); } else { let result = []; const marketIds = Object.keys (response); for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const trades = response[marketId]; const parsed = this.parseTrades (trades, market, since, limit); result = this.arrayConcat (result, parsed); } return result; } } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['start'] = parseInt (Math.floor (limit)); } const response = await this.publicPostReturnTradeHistory (this.extend (request, params)); // [ { type: 'buy', // date: '2019-07-25 11:24:41', // amount: '347.833140025692348611', // total: '0.050998794333719943', // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', // tid: 4320867, // timestamp: 1564053881, // price: '0.000146618560640751', // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', // orderHash: // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', // transactionHash: // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', // tokenBuy: '0x0000000000000000000000000000000000000000', // buyerFee: '0.695666280051384697', // gasFee: '28.986780264563232993', // sellerFee: '0.00005099879433372', // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', // usdValue: '11.336926687304238214' } ] return this.parseTrades (response, market, since, limit); } parseTrade (trade, market = undefined) { // { type: 'buy', // date: '2019-07-25 11:24:41', // amount: '347.833140025692348611', // total: '0.050998794333719943', // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', // tid: 4320867, // timestamp: 1564053881, // price: '0.000146618560640751', // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', // orderHash: // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', // transactionHash: // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', // tokenBuy: '0x0000000000000000000000000000000000000000', // buyerFee: '0.695666280051384697', // gasFee: '28.986780264563232993', // sellerFee: '0.00005099879433372', // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', // usdValue: '11.336926687304238214' } const side = this.safeString (trade, 'type'); let feeCurrency = undefined; let symbol = undefined; const maker = this.safeString (trade, 'maker'); let takerOrMaker = undefined; if (maker !== undefined && this.walletAddress !== undefined) { if (maker.toLowerCase () === this.walletAddress.toLowerCase ()) { takerOrMaker = 'maker'; } else { takerOrMaker = 'taker'; } } const buy = this.safeCurrencyCode (this.safeString (trade, 'tokenBuy')); const sell = this.safeCurrencyCode (this.safeString (trade, 'tokenSell')); // get ready to be mind-boggled let feeSide = undefined; if (buy !== undefined && sell !== undefined) { if (side === 'buy') { feeSide = 'buyerFee'; if (takerOrMaker === 'maker') { symbol = buy + '/' + sell; feeCurrency = buy; } else { symbol = sell + '/' + buy; feeCurrency = sell; } } else { feeSide = 'sellerFee'; if (takerOrMaker === 'maker') { symbol = sell + '/' + buy; feeCurrency = buy; } else { symbol = buy + '/' + sell; feeCurrency = sell; } } } if (symbol === undefined && market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeTimestamp (trade, 'timestamp'); const id = this.safeString (trade, 'tid'); const amount = this.safeFloat (trade, 'amount'); const price = this.safeFloat (trade, 'price'); const cost = this.safeFloat (trade, 'total'); let feeCost = this.safeFloat (trade, feeSide); if (feeCost < 0) { const gasFee = this.safeFloat (trade, 'gasFee'); feeCost = this.sum (gasFee, feeCost); } const fee = { 'currency': feeCurrency, 'cost': feeCost, }; if (feeCost !== undefined && amount !== undefined) { const feeCurrencyAmount = (feeCurrency === 'ETH') ? cost : amount; fee['rate'] = feeCost / feeCurrencyAmount; } const orderId = this.safeString (trade, 'orderHash'); return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': fee, 'price': price, 'amount': amount, 'cost': cost, 'takerOrMaker': takerOrMaker, 'side': side, 'order': orderId, 'symbol': symbol, 'type': 'limit', }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkRequiredDependencies (); this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const tokenAddress = currency['id']; const nonce = await this.getNonce (); amount = this.toWei (amount, currency['precision']); const requestToHash = { 'contractAddress': await this.getContractAddress (), 'token': tokenAddress, 'amount': amount, 'address': address, 'nonce': nonce, }; const hash = this.getIdexWithdrawHash (requestToHash); const signature = this.signMessage (hash, this.privateKey); const request = { 'address': address, 'amount': amount, 'token': tokenAddress, 'nonce': nonce, }; const response = await this.privatePostWithdraw (this.extend (request, signature)); // { amount: '0' } return { 'info': response, 'id': undefined, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { body = this.json (params); // all methods are POST const url = this.urls['api'] + '/' + path; headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', }; if (api === 'private') { this.checkRequiredCredentials (); headers['API-Key'] = this.apiKey; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } getIdexCreateOrderHash (order) { return this.soliditySha3 ([ order['contractAddress'], // address order['tokenBuy'], // address order['amountBuy'], // uint256 order['tokenSell'], // address order['amountSell'], // uint256 order['expires'], // uint256 order['nonce'], // uint256 order['address'], // address ]); } getIdexCancelOrderHash (order) { return this.soliditySha3 ([ order['orderHash'], // address order['nonce'], // uint256 ]); } getIdexMarketOrderHash (order) { return this.soliditySha3 ([ order['orderHash'], // address order['amount'], // uint256 order['address'], // address order['nonce'], // uint256 ]); } getIdexWithdrawHash (request) { return this.soliditySha3 ([ request['contractAddress'], // address request['token'], // uint256 request['amount'], // uint256 request['address'], // address request['nonce'], // uint256 ]); } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } if ('error' in response) { if (response['error'] in this.exceptions) { throw new this.exceptions[response['error']] (this.id + ' ' + response['error']); } throw new ExchangeError (this.id + ' ' + body); } } }; },{"./base/Exchange":7,"./base/errors":9}],96:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); // --------------------------------------------------------------------------- module.exports = class independentreserve extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'independentreserve', 'name': 'Independent Reserve', 'countries': [ 'AU', 'NZ' ], // Australia, New Zealand 'rateLimit': 1000, 'has': { 'CORS': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/30521662-cf3f477c-9bcb-11e7-89bc-d1ac85012eda.jpg', 'api': { 'public': 'https://api.independentreserve.com/Public', 'private': 'https://api.independentreserve.com/Private', }, 'www': 'https://www.independentreserve.com', 'doc': 'https://www.independentreserve.com/API', }, 'api': { 'public': { 'get': [ 'GetValidPrimaryCurrencyCodes', 'GetValidSecondaryCurrencyCodes', 'GetValidLimitOrderTypes', 'GetValidMarketOrderTypes', 'GetValidOrderTypes', 'GetValidTransactionTypes', 'GetMarketSummary', 'GetOrderBook', 'GetAllOrders', 'GetTradeHistorySummary', 'GetRecentTrades', 'GetFxRates', ], }, 'private': { 'post': [ 'PlaceLimitOrder', 'PlaceMarketOrder', 'CancelOrder', 'GetOpenOrders', 'GetClosedOrders', 'GetClosedFilledOrders', 'GetOrderDetails', 'GetAccounts', 'GetTransactions', 'GetDigitalCurrencyDepositAddress', 'GetDigitalCurrencyDepositAddresses', 'SynchDigitalCurrencyDepositAddressWithBlockchain', 'WithdrawDigitalCurrency', 'RequestFiatWithdrawal', 'GetTrades', 'GetBrokerageFees', ], }, }, 'fees': { 'trading': { 'taker': 0.5 / 100, 'maker': 0.5 / 100, 'percentage': true, 'tierBased': false, }, }, 'commonCurrencies': { 'PLA': 'PlayChip', }, }); } async fetchMarkets (params = {}) { const baseCurrencies = await this.publicGetGetValidPrimaryCurrencyCodes (params); const quoteCurrencies = await this.publicGetGetValidSecondaryCurrencyCodes (params); const result = []; for (let i = 0; i < baseCurrencies.length; i++) { const baseId = baseCurrencies[i]; const base = this.safeCurrencyCode (baseId); for (let j = 0; j < quoteCurrencies.length; j++) { const quoteId = quoteCurrencies[j]; const quote = this.safeCurrencyCode (quoteId); const id = baseId + '/' + quoteId; const symbol = base + '/' + quote; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': id, 'active': undefined, 'precision': this.precision, 'limits': this.limits, }); } } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const balances = await this.privatePostGetAccounts (params); const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'CurrencyCode'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'AvailableBalance'); account['total'] = this.safeFloat (balance, 'TotalBalance'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'primaryCurrencyCode': market['baseId'], 'secondaryCurrencyCode': market['quoteId'], }; const response = await this.publicGetGetOrderBook (this.extend (request, params)); const timestamp = this.parse8601 (this.safeString (response, 'CreatedTimestampUtc')); return this.parseOrderBook (response, timestamp, 'BuyOrders', 'SellOrders', 'Price', 'Volume'); } parseTicker (ticker, market = undefined) { const timestamp = this.parse8601 (this.safeString (ticker, 'CreatedTimestampUtc')); let symbol = undefined; if (market) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'LastPrice'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'DayHighestPrice'), 'low': this.safeFloat (ticker, 'DayLowestPrice'), 'bid': this.safeFloat (ticker, 'CurrentHighestBidPrice'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'CurrentLowestOfferPrice'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker, 'DayAvgPrice'), 'baseVolume': this.safeFloat (ticker, 'DayVolumeXbtInSecondaryCurrrency'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'primaryCurrencyCode': market['baseId'], 'secondaryCurrencyCode': market['quoteId'], }; const response = await this.publicGetGetMarketSummary (this.extend (request, params)); return this.parseTicker (response, market); } parseOrder (order, market = undefined) { // // { // "OrderGuid": "c7347e4c-b865-4c94-8f74-d934d4b0b177", // "CreatedTimestampUtc": "2014-09-23T12:39:34.3817763Z", // "Type": "MarketBid", // "VolumeOrdered": 5.0, // "VolumeFilled": 5.0, // "Price": null, // "AvgPrice": 100.0, // "ReservedAmount": 0.0, // "Status": "Filled", // "PrimaryCurrencyCode": "Xbt", // "SecondaryCurrencyCode": "Usd" // } // let symbol = undefined; const baseId = this.safeString (order, 'PrimaryCurrencyCode'); const quoteId = this.safeString (order, 'PrimaryCurrencyCode'); let base = undefined; let quote = undefined; if ((baseId !== undefined) && (quoteId !== undefined)) { base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } else if (market !== undefined) { symbol = market['symbol']; base = market['base']; quote = market['quote']; } let orderType = this.safeValue (order, 'Type'); if (orderType.indexOf ('Market') >= 0) { orderType = 'market'; } else if (orderType.indexOf ('Limit') >= 0) { orderType = 'limit'; } let side = undefined; if (orderType.indexOf ('Bid') >= 0) { side = 'buy'; } else if (orderType.indexOf ('Offer') >= 0) { side = 'sell'; } const timestamp = this.parse8601 (this.safeString (order, 'CreatedTimestampUtc')); let amount = this.safeFloat (order, 'VolumeOrdered'); if (amount === undefined) { amount = this.safeFloat (order, 'Volume'); } const filled = this.safeFloat (order, 'VolumeFilled'); let remaining = undefined; const feeRate = this.safeFloat (order, 'FeePercent'); let feeCost = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = amount - filled; if (feeRate !== undefined) { feeCost = feeRate * filled; } } } const fee = { 'rate': feeRate, 'cost': feeCost, 'currency': base, }; const id = this.safeString (order, 'OrderGuid'); const status = this.parseOrderStatus (this.safeString (order, 'Status')); const cost = this.safeFloat (order, 'Value'); const average = this.safeFloat (order, 'AvgPrice'); const price = this.safeFloat (order, 'Price', average); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': orderType, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } parseOrderStatus (status) { const statuses = { 'Open': 'open', 'PartiallyFilled': 'open', 'Filled': 'closed', 'PartiallyFilledAndCancelled': 'canceled', 'Cancelled': 'canceled', 'PartiallyFilledAndExpired': 'canceled', 'Expired': 'canceled', }; return this.safeString (statuses, status, status); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const response = await this.privatePostGetOrderDetails (this.extend ({ 'orderGuid': id, }, params)); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } return this.parseOrder (response, market); } async fetchMyTrades (symbol = undefined, since = undefined, limit = 50, params = {}) { await this.loadMarkets (); const pageIndex = this.safeInteger (params, 'pageIndex', 1); if (limit === undefined) { limit = 50; } const request = this.ordered ({ 'pageIndex': pageIndex, 'pageSize': limit, }); const response = await this.privatePostGetTrades (this.extend (request, params)); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } return this.parseTrades (response['Data'], market, since, limit); } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (trade['TradeTimestampUtc']); const id = this.safeString (trade, 'TradeGuid'); const orderId = this.safeString (trade, 'OrderGuid'); const price = this.safeFloat2 (trade, 'Price', 'SecondaryCurrencyTradePrice'); const amount = this.safeFloat2 (trade, 'VolumeTraded', 'PrimaryCurrencyAmount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } let side = this.safeString (trade, 'OrderType'); if (side !== undefined) { if (side.indexOf ('Bid') >= 0) { side = 'buy'; } else if (side.indexOf ('Offer') >= 0) { side = 'sell'; } } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'primaryCurrencyCode': market['baseId'], 'secondaryCurrencyCode': market['quoteId'], 'numberOfRecentTradesToRetrieve': 50, // max = 50 }; const response = await this.publicGetGetRecentTrades (this.extend (request, params)); return this.parseTrades (response['Trades'], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const capitalizedOrderType = this.capitalize (type); const method = 'privatePostPlace' + capitalizedOrderType + 'Order'; let orderType = capitalizedOrderType; orderType += (side === 'sell') ? 'Offer' : 'Bid'; const request = this.ordered ({ 'primaryCurrencyCode': market['baseId'], 'secondaryCurrencyCode': market['quoteId'], 'orderType': orderType, }); if (type === 'limit') { request['price'] = price; } request['volume'] = amount; const response = await this[method] (this.extend (request, params)); return { 'info': response, 'id': response['OrderGuid'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderGuid': id, }; return await this.privatePostCancelOrder (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); const auth = [ url, 'apiKey=' + this.apiKey, 'nonce=' + nonce.toString (), ]; const keys = Object.keys (params); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = params[key].toString (); auth.push (key + '=' + value); } const message = auth.join (','); const signature = this.hmac (this.encode (message), this.encode (this.secret)); const query = this.ordered ({}); query['apiKey'] = this.apiKey; query['nonce'] = nonce; query['signature'] = signature.toUpperCase (); for (let i = 0; i < keys.length; i++) { const key = keys[i]; query[key] = params[key]; } body = this.json (query); headers = { 'Content-Type': 'application/json' }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7}],97:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, InsufficientFunds, InvalidOrder, OrderNotFound, AuthenticationError, BadSymbol } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class indodax extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'indodax', 'name': 'INDODAX', 'countries': [ 'ID' ], // Indonesia 'has': { 'CORS': false, 'createMarketOrder': false, 'fetchTickers': false, 'fetchOrder': true, 'fetchOrders': false, 'fetchClosedOrders': true, 'fetchOpenOrders': true, 'fetchMyTrades': false, 'fetchCurrencies': false, 'withdraw': true, }, 'version': '1.8', // as of 9 April 2018 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/37443283-2fddd0e4-281c-11e8-9741-b4f1419001b5.jpg', 'api': { 'public': 'https://indodax.com/api', 'private': 'https://indodax.com/tapi', }, 'www': 'https://www.indodax.com', 'doc': 'https://indodax.com/downloads/BITCOINCOID-API-DOCUMENTATION.pdf', 'referral': 'https://indodax.com/ref/testbitcoincoid/1', }, 'api': { 'public': { 'get': [ '{pair}/ticker', '{pair}/trades', '{pair}/depth', ], }, 'private': { 'post': [ 'getInfo', 'transHistory', 'trade', 'tradeHistory', 'getOrder', 'openOrders', 'cancelOrder', 'orderHistory', 'withdrawCoin', ], }, }, 'markets': { // HARDCODING IS DEPRECATED // but they don't have a corresponding endpoint in their API // IDR markets 'BTC/IDR': { 'id': 'btc_idr', 'symbol': 'BTC/IDR', 'base': 'BTC', 'quote': 'IDR', 'baseId': 'btc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.0001, 'max': undefined }}}, 'TEN/IDR': { 'id': 'ten_idr', 'symbol': 'TEN/IDR', 'base': 'TEN', 'quote': 'IDR', 'baseId': 'ten', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'ABYSS/IDR': { 'id': 'abyss_idr', 'symbol': 'ABYSS/IDR', 'base': 'ABYSS', 'quote': 'IDR', 'baseId': 'abyss', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'ACT/IDR': { 'id': 'act_idr', 'symbol': 'ACT/IDR', 'base': 'ACT', 'quote': 'IDR', 'baseId': 'act', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'ADA/IDR': { 'id': 'ada_idr', 'symbol': 'ADA/IDR', 'base': 'ADA', 'quote': 'IDR', 'baseId': 'ada', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'AOA/IDR': { 'id': 'aoa_idr', 'symbol': 'AOA/IDR', 'base': 'AOA', 'quote': 'IDR', 'baseId': 'aoa', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'ATOM/IDR': { 'id': 'atom_idr', 'symbol': 'ATOM/IDR', 'base': 'ATOM', 'quote': 'IDR', 'baseId': 'atom', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'BAT/IDR': { 'id': 'bat_idr', 'symbol': 'BAT/IDR', 'base': 'BAT', 'quote': 'IDR', 'baseId': 'bat', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'BCD/IDR': { 'id': 'bcd_idr', 'symbol': 'BCD/IDR', 'base': 'BCD', 'quote': 'IDR', 'baseId': 'bcd', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'BCH/IDR': { 'id': 'bchabc_idr', 'symbol': 'BCH/IDR', 'base': 'BCH', 'quote': 'IDR', 'baseId': 'bchabc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, 'BSV/IDR': { 'id': 'bchsv_idr', 'symbol': 'BSV/IDR', 'base': 'BSV', 'quote': 'IDR', 'baseId': 'bchsv', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, 'BNB/IDR': { 'id': 'bnb_idr', 'symbol': 'BNB/IDR', 'base': 'BNB', 'quote': 'IDR', 'baseId': 'bnb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, 'BTG/IDR': { 'id': 'btg_idr', 'symbol': 'BTG/IDR', 'base': 'BTG', 'quote': 'IDR', 'baseId': 'btg', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'BTS/IDR': { 'id': 'bts_idr', 'symbol': 'BTS/IDR', 'base': 'BTS', 'quote': 'IDR', 'baseId': 'bts', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'BTT/IDR': { 'id': 'btt_idr', 'symbol': 'BTT/IDR', 'base': 'BTT', 'quote': 'IDR', 'baseId': 'btt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1000, 'max': undefined }}}, 'COAL/IDR': { 'id': 'coal_idr', 'symbol': 'COAL/IDR', 'base': 'COAL', 'quote': 'IDR', 'baseId': 'coal', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 50, 'max': undefined }}}, 'CRO/IDR': { 'id': 'cro_idr', 'symbol': 'CRO/IDR', 'base': 'CRO', 'quote': 'IDR', 'baseId': 'cro', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'DASH/IDR': { 'id': 'drk_idr', 'symbol': 'DASH/IDR', 'base': 'DASH', 'quote': 'IDR', 'baseId': 'drk', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'DAX/IDR': { 'id': 'dax_idr', 'symbol': 'DAX/IDR', 'base': 'DAX', 'quote': 'IDR', 'baseId': 'dax', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'DOGE/IDR': { 'id': 'doge_idr', 'symbol': 'DOGE/IDR', 'base': 'DOGE', 'quote': 'IDR', 'baseId': 'doge', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1000, 'max': undefined }}}, 'ETH/IDR': { 'id': 'eth_idr', 'symbol': 'ETH/IDR', 'base': 'ETH', 'quote': 'IDR', 'baseId': 'eth', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'EOS/IDR': { 'id': 'eos_idr', 'symbol': 'EOS/IDR', 'base': 'EOS', 'quote': 'IDR', 'baseId': 'eos', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'ETC/IDR': { 'id': 'etc_idr', 'symbol': 'ETC/IDR', 'base': 'ETC', 'quote': 'IDR', 'baseId': 'etc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, 'GARD/IDR': { 'id': 'gard_idr', 'symbol': 'GARD/IDR', 'base': 'GARD', 'quote': 'IDR', 'baseId': 'gard', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, 'GSC/IDR': { 'id': 'gsc_idr', 'symbol': 'GSC/IDR', 'base': 'GSC', 'quote': 'IDR', 'baseId': 'gsc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, 'GXC/IDR': { 'id': 'gxs_idr', 'symbol': 'GXC/IDR', 'base': 'GXC', 'quote': 'IDR', 'baseId': 'gxs', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, 'HPB/IDR': { 'id': 'hpb_idr', 'symbol': 'HPB/IDR', 'base': 'HPB', 'quote': 'IDR', 'baseId': 'hpb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'IGNIS/IDR': { 'id': 'ignis_idr', 'symbol': 'IGNIS/IDR', 'base': 'IGNIS', 'quote': 'IDR', 'baseId': 'ignis', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, 'INX/IDR': { 'id': 'inx_idr', 'symbol': 'INX/IDR', 'base': 'INX', 'quote': 'IDR', 'baseId': 'inx', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, 'IOTA/IDR': { 'id': 'iota_idr', 'symbol': 'IOTA/IDR', 'base': 'IOTA', 'quote': 'IDR', 'baseId': 'iota', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'LINK/IDR': { 'id': 'link_idr', 'symbol': 'LINK/IDR', 'base': 'LINK', 'quote': 'IDR', 'baseId': 'link', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, 'LTC/IDR': { 'id': 'ltc_idr', 'symbol': 'LTC/IDR', 'base': 'LTC', 'quote': 'IDR', 'baseId': 'ltc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'MBL/IDR': { 'id': 'mbl_idr', 'symbol': 'MBL/IDR', 'base': 'MBL', 'quote': 'IDR', 'baseId': 'mbl', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'NEO/IDR': { 'id': 'neo_idr', 'symbol': 'NEO/IDR', 'base': 'NEO', 'quote': 'IDR', 'baseId': 'neo', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'NPXS/IDR': { 'id': 'npxs_idr', 'symbol': 'NPXS/IDR', 'base': 'NPXS', 'quote': 'IDR', 'baseId': 'npxs', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, 'NXT/IDR': { 'id': 'nxt_idr', 'symbol': 'NXT/IDR', 'base': 'NXT', 'quote': 'IDR', 'baseId': 'nxt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'OKB/IDR': { 'id': 'okb_idr', 'symbol': 'OKB/IDR', 'base': 'OKB', 'quote': 'IDR', 'baseId': 'okb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.5, 'max': undefined }}}, 'ONT/IDR': { 'id': 'ont_idr', 'symbol': 'ONT/IDR', 'base': 'ONT', 'quote': 'IDR', 'baseId': 'ont', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'PXG/IDR': { 'id': 'pxg_idr', 'symbol': 'PXG/IDR', 'base': 'PXG', 'quote': 'IDR', 'baseId': 'pxg', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'QTUM/IDR': { 'id': 'qtum_idr', 'symbol': 'QTUM/IDR', 'base': 'QTUM', 'quote': 'IDR', 'baseId': 'qtum', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'RVN/IDR': { 'id': 'rvn_idr', 'symbol': 'RVN/IDR', 'base': 'RVN', 'quote': 'IDR', 'baseId': 'rvn', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'SSP/IDR': { 'id': 'ssp_idr', 'symbol': 'SSP/IDR', 'base': 'SSP', 'quote': 'IDR', 'baseId': 'ssp', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, 'SUMO/IDR': { 'id': 'sumo_idr', 'symbol': 'SUMO/IDR', 'base': 'SUMO', 'quote': 'IDR', 'baseId': 'sumo', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, // 'STQ/IDR': { 'id': 'stq_idr', 'symbol': 'STQ/IDR', 'base': 'STQ', 'quote': 'IDR', 'baseId': 'stq', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'TRX/IDR': { 'id': 'trx_idr', 'symbol': 'TRX/IDR', 'base': 'TRX', 'quote': 'IDR', 'baseId': 'trx', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'USDC/IDR': { 'id': 'usdc_idr', 'symbol': 'USDC/IDR', 'base': 'USDC', 'quote': 'IDR', 'baseId': 'usdc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'USDT/IDR': { 'id': 'usdt_idr', 'symbol': 'USDT/IDR', 'base': 'USDT', 'quote': 'IDR', 'baseId': 'usdt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'VEX/IDR': { 'id': 'vex_idr', 'symbol': 'VEX/IDR', 'base': 'VEX', 'quote': 'IDR', 'baseId': 'vex', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, 'VIDY/IDR': { 'id': 'vidy_idr', 'symbol': 'VIDY/IDR', 'base': 'VIDY', 'quote': 'IDR', 'baseId': 'vidy', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 100, 'max': undefined }}}, 'WAVES/IDR': { 'id': 'waves_idr', 'symbol': 'WAVES/IDR', 'base': 'WAVES', 'quote': 'IDR', 'baseId': 'waves', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, 'XEM/IDR': { 'id': 'nem_idr', 'symbol': 'XEM/IDR', 'base': 'XEM', 'quote': 'IDR', 'baseId': 'nem', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, 'XLM/IDR': { 'id': 'str_idr', 'symbol': 'XLM/IDR', 'base': 'XLM', 'quote': 'IDR', 'baseId': 'str', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 20, 'max': undefined }}}, 'XDCE/IDR': { 'id': 'xdce_idr', 'symbol': 'XDCE/IDR', 'base': 'XDCE', 'quote': 'IDR', 'baseId': 'xdce', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 10, 'max': undefined }}}, 'XMR/IDR': { 'id': 'xmr_idr', 'symbol': 'XMR/IDR', 'base': 'XMR', 'quote': 'IDR', 'baseId': 'xmr', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'XRP/IDR': { 'id': 'xrp_idr', 'symbol': 'XRP/IDR', 'base': 'XRP', 'quote': 'IDR', 'baseId': 'xrp', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 10, 'max': undefined }}}, 'XZC/IDR': { 'id': 'xzc_idr', 'symbol': 'XZC/IDR', 'base': 'XZC', 'quote': 'IDR', 'baseId': 'xzc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, 'VSYS/IDR': { 'id': 'vsys_idr', 'symbol': 'VSYS/IDR', 'base': 'VSYS', 'quote': 'IDR', 'baseId': 'vsys', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, 'ZEC/IDR': { 'id': 'zec_idr', 'symbol': 'ZEC/IDR', 'base': 'ZEC', 'quote': 'IDR', 'baseId': 'zec', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, // BTC markets 'BTS/BTC': { 'id': 'bts_btc', 'symbol': 'BTS/BTC', 'base': 'BTS', 'quote': 'BTC', 'baseId': 'bts', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'DASH/BTC': { 'id': 'drk_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'drk', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 6 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'DOGE/BTC': { 'id': 'doge_btc', 'symbol': 'DOGE/BTC', 'base': 'DOGE', 'quote': 'BTC', 'baseId': 'doge', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 5 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 6 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'NXT/BTC': { 'id': 'nxt_btc', 'symbol': 'NXT/BTC', 'base': 'NXT', 'quote': 'BTC', 'baseId': 'nxt', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'SUMO/BTC': { 'id': 'sumo_btc', 'symbol': 'SUMO/BTC', 'base': 'SUMO', 'quote': 'BTC', 'baseId': 'sumo', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'TEN/BTC': { 'id': 'ten_btc', 'symbol': 'TEN/BTC', 'base': 'TEN', 'quote': 'BTC', 'baseId': 'ten', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'XEM/BTC': { 'id': 'nem_btc', 'symbol': 'XEM/BTC', 'base': 'XEM', 'quote': 'BTC', 'baseId': 'nem', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, 'XLM/BTC': { 'id': 'str_btc', 'symbol': 'XLM/BTC', 'base': 'XLM', 'quote': 'BTC', 'baseId': 'str', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, 'XRP/BTC': { 'id': 'xrp_btc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0, 'taker': 0.003, }, }, 'exceptions': { 'exact': { 'invalid_pair': BadSymbol, // {"error":"invalid_pair","error_description":"Invalid Pair"} 'Insufficient balance.': InsufficientFunds, 'invalid order.': OrderNotFound, 'Invalid credentials. API not found or session has expired.': AuthenticationError, 'Invalid credentials. Bad sign.': AuthenticationError, }, 'broad': { 'Minimum price': InvalidOrder, 'Minimum order': InvalidOrder, }, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGetInfo (params); const balances = this.safeValue (response, 'return', {}); const free = this.safeValue (balances, 'balance', {}); const used = this.safeValue (balances, 'balance_hold', {}); const result = { 'info': response }; const currencyIds = Object.keys (free); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (free, currencyId); account['used'] = this.safeFloat (used, currencyId); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; const orderbook = await this.publicGetPairDepth (this.extend (request, params)); return this.parseOrderBook (orderbook, undefined, 'buy', 'sell'); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetPairTicker (this.extend (request, params)); // // { // "ticker": { // "high":"0.01951", // "low":"0.01877", // "vol_eth":"39.38839319", // "vol_btc":"0.75320886", // "last":"0.01896", // "buy":"0.01896", // "sell":"0.019", // "server_time":1565248908 // } // } // const ticker = response['ticker']; const timestamp = this.safeTimestamp (ticker, 'server_time'); const baseVolume = 'vol_' + market['baseId'].toLowerCase (); const quoteVolume = 'vol_' + market['quoteId'].toLowerCase (); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, baseVolume), 'quoteVolume': this.safeFloat (ticker, quoteVolume), 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const id = this.safeString (trade, 'tid'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const type = undefined; const side = this.safeString (trade, 'type'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetPairTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseOrder (order, market = undefined) { // // { // "order_id": "12345", // "submit_time": "1392228122", // "price": "8000000", // "type": "sell", // "order_ltc": "100000000", // "remain_ltc": "100000000" // } // let side = undefined; if ('type' in order) { side = order['type']; } let status = this.safeString (order, 'status', 'open'); if (status === 'filled') { status = 'closed'; } else if (status === 'cancelled') { status = 'canceled'; } let symbol = undefined; let cost = undefined; const price = this.safeFloat (order, 'price'); let amount = undefined; let remaining = undefined; let filled = undefined; if (market !== undefined) { symbol = market['symbol']; let quoteId = market['quoteId']; let baseId = market['baseId']; if ((market['quoteId'] === 'idr') && ('order_rp' in order)) { quoteId = 'rp'; } if ((market['baseId'] === 'idr') && ('remain_rp' in order)) { baseId = 'rp'; } cost = this.safeFloat (order, 'order_' + quoteId); if (cost) { amount = cost / price; const remainingCost = this.safeFloat (order, 'remain_' + quoteId); if (remainingCost !== undefined) { remaining = remainingCost / price; filled = amount - remaining; } } else { amount = this.safeFloat (order, 'order_' + baseId); cost = price * amount; remaining = this.safeFloat (order, 'remain_' + baseId); filled = amount - remaining; } } let average = undefined; if (filled) { average = cost / filled; } const timestamp = this.safeInteger (order, 'submit_time'); const fee = undefined; const id = this.safeString (order, 'order_id'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'order_id': id, }; const response = await this.privatePostGetOrder (this.extend (request, params)); const orders = response['return']; const order = this.parseOrder (this.extend ({ 'id': id }, orders['order']), market); return this.extend ({ 'info': response }, order); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } const response = await this.privatePostOpenOrders (this.extend (request, params)); const rawOrders = response['return']['orders']; // { success: 1, return: { orders: null }} if no orders if (!rawOrders) { return []; } // { success: 1, return: { orders: [ ... objects ] }} for orders fetched by symbol if (symbol !== undefined) { return this.parseOrders (rawOrders, market, since, limit); } // { success: 1, return: { orders: { marketid: [ ... objects ] }}} if all orders are fetched const marketIds = Object.keys (rawOrders); let exchangeOrders = []; for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const marketOrders = rawOrders[marketId]; market = this.markets_by_id[marketId]; const parsedOrders = this.parseOrders (marketOrders, market, since, limit); exchangeOrders = this.arrayConcat (exchangeOrders, parsedOrders); } return exchangeOrders; } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); } await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } const response = await this.privatePostOrderHistory (this.extend (request, params)); let orders = this.parseOrders (response['return']['orders'], market, since, limit); orders = this.filterBy (orders, 'status', 'closed'); if (symbol !== undefined) { return this.filterBySymbol (orders, symbol); } return orders; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type !== 'limit') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'type': side, 'price': price, }; const currency = market['baseId']; if (side === 'buy') { request[market['quoteId']] = amount * price; } else { request[market['baseId']] = amount; } request[currency] = amount; const result = await this.privatePostTrade (this.extend (request, params)); return { 'info': result, 'id': result['return']['order_id'].toString (), }; } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } const side = this.safeValue (params, 'side'); if (side === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires an extra "side" param'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_id': id, 'pair': market['id'], 'type': side, }; return await this.privatePostCancelOrder (this.extend (request, params)); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); // Custom string you need to provide to identify each withdrawal. // Will be passed to callback URL (assigned via website to the API key) // so your system can identify the request and confirm it. // Alphanumeric, max length 255. const requestId = this.milliseconds (); // Alternatively: // let requestId = this.uuid (); const request = { 'currency': currency['id'], 'withdraw_amount': amount, 'withdraw_address': address, 'request_id': requestId.toString (), }; if (tag) { request['withdraw_memo'] = tag; } const response = await this.privatePostWithdrawCoin (this.extend (request, params)); // // { // "success": 1, // "status": "approved", // "withdraw_currency": "xrp", // "withdraw_address": "rwWr7KUZ3ZFwzgaDGjKBysADByzxvohQ3C", // "withdraw_amount": "10000.00000000", // "fee": "2.00000000", // "amount_after_fee": "9998.00000000", // "submit_time": "1509469200", // "withdraw_id": "xrp-12345", // "txid": "", // "withdraw_memo": "123123" // } // let id = undefined; if (('txid' in response) && (response['txid'].length > 0)) { id = response['txid']; } return { 'info': response, 'id': id, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; if (api === 'public') { url += '/' + this.implodeParams (path, params); } else { this.checkRequiredCredentials (); body = this.urlencode (this.extend ({ 'method': path, 'nonce': this.nonce (), }, params)); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } // { success: 0, error: "invalid order." } // or // [{ data, ... }, { ... }, ... ] if (Array.isArray (response)) { return; // public endpoints may return []-arrays } const error = this.safeValue (response, 'error', ''); if (!('success' in response) && error === '') { return; // no 'success' property on public responses } if (this.safeInteger (response, 'success', 0) === 1) { // { success: 1, return: { orders: [] }} if (!('return' in response)) { throw new ExchangeError (this.id + ': malformed response: ' + this.json (response)); } else { return; } } const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); throw new ExchangeError (feedback); // unknown message } }; },{"./base/Exchange":7,"./base/errors":9}],98:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class itbit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'itbit', 'name': 'itBit', 'countries': [ 'US' ], 'rateLimit': 2000, 'version': 'v1', 'has': { 'CORS': true, 'createMarketOrder': false, 'fetchMyTrades': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27822159-66153620-60ad-11e7-89e7-005f6d7f3de0.jpg', 'api': 'https://api.itbit.com', 'www': 'https://www.itbit.com', 'doc': [ 'https://api.itbit.com/docs', 'https://www.itbit.com/api', ], }, 'api': { 'public': { 'get': [ 'markets/{symbol}/ticker', 'markets/{symbol}/order_book', 'markets/{symbol}/trades', ], }, 'private': { 'get': [ 'wallets', 'wallets/{walletId}', 'wallets/{walletId}/balances/{currencyCode}', 'wallets/{walletId}/funding_history', 'wallets/{walletId}/trades', 'wallets/{walletId}/orders', 'wallets/{walletId}/orders/{id}', ], 'post': [ 'wallet_transfers', 'wallets', 'wallets/{walletId}/cryptocurrency_deposits', 'wallets/{walletId}/cryptocurrency_withdrawals', 'wallets/{walletId}/orders', 'wire_withdrawal', ], 'delete': [ 'wallets/{walletId}/orders/{id}', ], }, }, 'markets': { 'BTC/USD': { 'id': 'XBTUSD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'XBT', 'quoteId': 'USD' }, 'BTC/SGD': { 'id': 'XBTSGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD', 'baseId': 'XBT', 'quoteId': 'SGD' }, 'BTC/EUR': { 'id': 'XBTEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'XBT', 'quoteId': 'EUR' }, 'ETH/USD': { 'id': 'ETHUSD', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD', 'baseId': 'ETH', 'quoteId': 'USD' }, 'ETH/EUR': { 'id': 'ETHEUR', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR', 'baseId': 'ETH', 'quoteId': 'EUR' }, 'ETH/SGD': { 'id': 'ETHSGD', 'symbol': 'ETH/SGD', 'base': 'ETH', 'quote': 'SGD', 'baseId': 'ETH', 'quoteId': 'SGD' }, }, 'fees': { 'trading': { 'maker': -0.03 / 100, 'taker': 0.35 / 100, }, }, 'commonCurrencies': { 'XBT': 'BTC', }, }); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; const orderbook = await this.publicGetMarketsSymbolOrderBook (this.extend (request, params)); return this.parseOrderBook (orderbook); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; const ticker = await this.publicGetMarketsSymbolTicker (this.extend (request, params)); const serverTimeUTC = this.safeString (ticker, 'serverTimeUTC'); if (!serverTimeUTC) { throw new ExchangeError (this.id + ' fetchTicker returned a bad response: ' + this.json (ticker)); } const timestamp = this.parse8601 (serverTimeUTC); const vwap = this.safeFloat (ticker, 'vwap24h'); const baseVolume = this.safeFloat (ticker, 'volume24h'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'lastPrice'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high24h'), 'low': this.safeFloat (ticker, 'low24h'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': this.safeFloat (ticker, 'openToday'), 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // timestamp: "2015-05-22T17:45:34.7570000Z", // matchNumber: "5CR1JEUBBM8J", // price: "351.45000000", // amount: "0.00010000" // } // // fetchMyTrades (private) // // { // "orderId": "248ffda4-83a0-4033-a5bb-8929d523f59f", // "timestamp": "2015-05-11T14:48:01.9870000Z", // "instrument": "XBTUSD", // "direction": "buy", // buy or sell // "currency1": "XBT", // base currency // "currency1Amount": "0.00010000", // order amount in base currency // "currency2": "USD", // quote currency // "currency2Amount": "0.0250530000000000", // order cost in quote currency // "rate": "250.53000000", // "commissionPaid": "0.00000000", // net trade fee paid after using any available rebate balance // "commissionCurrency": "USD", // "rebatesApplied": "-0.000125265", // negative values represent amount of rebate balance used for trades removing liquidity from order book; positive values represent amount of rebate balance earned from trades adding liquidity to order book // "rebateCurrency": "USD", // "executionId": "23132" // } // const id = this.safeString2 (trade, 'executionId', 'matchNumber'); const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); const side = this.safeString (trade, 'direction'); const orderId = this.safeString (trade, 'orderId'); let feeCost = this.safeFloat (trade, 'commissionPaid'); const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); const feeCurrency = this.safeCurrencyCode (feeCurrencyId); let rebatesApplied = this.safeFloat (trade, 'rebatesApplied'); if (rebatesApplied !== undefined) { rebatesApplied = -rebatesApplied; } const rebateCurrencyId = this.safeString (trade, 'rebateCurrency'); const rebateCurrency = this.safeCurrencyCode (rebateCurrencyId); const price = this.safeFloat2 (trade, 'price', 'rate'); const amount = this.safeFloat2 (trade, 'currency1Amount', 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; const marketId = this.safeString (trade, 'instrument'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const baseId = this.safeString (trade, 'currency1'); const quoteId = this.safeString (trade, 'currency2'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const result = { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; if (feeCost !== undefined) { if (rebatesApplied !== undefined) { if (feeCurrency === rebateCurrency) { feeCost = this.sum (feeCost, rebatesApplied); result['fee'] = { 'cost': feeCost, 'currency': feeCurrency, }; } else { result['fees'] = [ { 'cost': feeCost, 'currency': feeCurrency, }, { 'cost': rebatesApplied, 'currency': rebateCurrency, }, ]; } } else { result['fee'] = { 'cost': feeCost, 'currency': feeCurrency, }; } } if (!('fee' in result)) { if (!('fees' in result)) { result['fee'] = undefined; } } return result; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const walletId = this.safeString (params, 'walletId'); if (walletId === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a walletId parameter'); } const request = { 'walletId': walletId, }; if (limit !== undefined) { request['perPage'] = limit; // default 50, max 50 } const response = await this.privateGetWalletsWalletIdFundingHistory (this.extend (request, params)); // { bankName: 'USBC (usd)', // withdrawalId: 94740, // holdingPeriodCompletionDate: '2018-04-16T07:57:05.9606869', // time: '2018-04-16T07:57:05.9600000', // currency: 'USD', // transactionType: 'Withdrawal', // amount: '2186.72000000', // walletName: 'Wallet', // status: 'completed' }, // // { "time": "2018-01-02T19:52:22.4176503", // "amount": "0.50000000", // "status": "completed", // "txnHash": "1b6fff67ed83cb9e9a38ca4976981fc047322bc088430508fe764a127d3ace95", // "currency": "XBT", // "walletName": "Wallet", // "transactionType": "Deposit", // "destinationAddress": "3AAWTH9et4e8o51YKp9qPpmujrNXKwHWNX"} const items = response['fundingHistory']; const result = []; for (let i = 0; i < items.length; i++) { const item = items[i]; const time = this.safeString (item, 'time'); const timestamp = this.parse8601 (time); const currency = this.safeString (item, 'currency'); const destinationAddress = this.safeString (item, 'destinationAddress'); const txnHash = this.safeString (item, 'txnHash'); const transactionType = this.safeStringLower (item, 'transactionType'); const transactionStatus = this.safeString (item, 'status'); const status = this.parseTransferStatus (transactionStatus); result.push ({ 'id': this.safeString (item, 'withdrawalId'), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'currency': this.safeCurrencyCode (currency), 'address': destinationAddress, 'tag': undefined, 'txid': txnHash, 'type': transactionType, 'status': status, 'amount': this.safeFloat (item, 'amount'), 'fee': undefined, 'info': item, }); } return result; } parseTransferStatus (status) { const options = { 'cancelled': 'canceled', 'completed': 'ok', }; return this.safeString (options, status, 'pending'); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const walletId = this.safeString (params, 'walletId'); if (walletId === undefined) { throw new ExchangeError (this.id + ' fetchMyTrades requires a walletId parameter'); } const request = { 'walletId': walletId, }; if (since !== undefined) { request['rangeStart'] = this.ymdhms (since, 'T'); } if (limit !== undefined) { request['perPage'] = limit; // default 50, max 50 } const response = await this.privateGetWalletsWalletIdTrades (this.extend (request, params)); // // { // "totalNumberOfRecords": "2", // "currentPageNumber": "1", // "latestExecutionId": "332", // most recent execution at time of response // "recordsPerPage": "50", // "tradingHistory": [ // { // "orderId": "248ffda4-83a0-4033-a5bb-8929d523f59f", // "timestamp": "2015-05-11T14:48:01.9870000Z", // "instrument": "XBTUSD", // "direction": "buy", // buy or sell // "currency1": "XBT", // base currency // "currency1Amount": "0.00010000", // order amount in base currency // "currency2": "USD", // quote currency // "currency2Amount": "0.0250530000000000", // order cost in quote currency // "rate": "250.53000000", // "commissionPaid": "0.00000000", // net trade fee paid after using any available rebate balance // "commissionCurrency": "USD", // "rebatesApplied": "-0.000125265", // negative values represent amount of rebate balance used for trades removing liquidity from order book; positive values represent amount of rebate balance earned from trades adding liquidity to order book // "rebateCurrency": "USD", // "executionId": "23132" // }, // ], // } // const trades = this.safeValue (response, 'tradingHistory', []); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } return this.parseTrades (trades, market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetMarketsSymbolTrades (this.extend (request, params)); // // { // count: 3, // recentTrades: [ // { // timestamp: "2015-05-22T17:45:34.7570000Z", // matchNumber: "5CR1JEUBBM8J", // price: "351.45000000", // amount: "0.00010000" // }, // ] // } // const trades = this.safeValue (response, 'recentTrades', []); return this.parseTrades (trades, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.fetchWallets (params); const balances = response[0]['balances']; const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'availableBalance'); account['total'] = this.safeFloat (balance, 'totalBalance'); result[code] = account; } return this.parseBalance (result); } async fetchWallets (params = {}) { await this.loadMarkets (); if (!this.uid) { throw new AuthenticationError (this.id + ' fetchWallets requires uid API credential'); } const request = { 'userId': this.uid, }; return await this.privateGetWallets (this.extend (request, params)); } async fetchWallet (walletId, params = {}) { await this.loadMarkets (); const request = { 'walletId': walletId, }; return await this.privateGetWalletsWalletId (this.extend (request, params)); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': 'open', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': 'filled', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const walletIdInParams = ('walletId' in params); if (!walletIdInParams) { throw new ExchangeError (this.id + ' fetchOrders requires a walletId parameter'); } const walletId = params['walletId']; const request = { 'walletId': walletId, }; const response = await this.privateGetWalletsWalletIdOrders (this.extend (request, params)); return this.parseOrders (response, market, since, limit); } parseOrderStatus (status) { const statuses = { 'submitted': 'open', // order pending book entry 'open': 'open', 'filled': 'closed', 'cancelled': 'canceled', 'rejected': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "id": "13d6af57-8b0b-41e5-af30-becf0bcc574d", // "walletId": "7e037345-1288-4c39-12fe-d0f99a475a98", // "side": "buy", // "instrument": "XBTUSD", // "type": "limit", // "currency": "XBT", // "amount": "2.50000000", // "displayAmount": "2.50000000", // "price": "650.00000000", // "volumeWeightedAveragePrice": "0.00000000", // "amountFilled": "0.00000000", // "createdTime": "2014-02-11T17:05:15Z", // "status": "submitted", // "funds": null, // "metadata": {}, // "clientOrderIdentifier": null, // "postOnly": "False" // } // const side = this.safeString (order, 'side'); const type = this.safeString (order, 'type'); const symbol = this.markets_by_id[order['instrument']]['symbol']; const timestamp = this.parse8601 (order['createdTime']); const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'amountFilled'); let remaining = undefined; let cost = undefined; const fee = undefined; const price = this.safeFloat (order, 'price'); const average = this.safeFloat (order, 'volumeWeightedAveragePrice'); if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; } if (average !== undefined) { cost = filled * average; } } const clientOrderId = this.safeString (order, 'clientOrderIdentifier'); const id = this.safeString (order, 'id'); return { 'id': id, 'clientOrderId': clientOrderId, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': this.parseOrderStatus (this.safeString (order, 'status')), 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'fee': fee, // 'trades': this.parseTrades (order['trades'], market), 'trades': undefined, }; } nonce () { return this.milliseconds (); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } const walletIdInParams = ('walletId' in params); if (!walletIdInParams) { throw new ExchangeError (this.id + ' createOrder requires a walletId parameter'); } amount = amount.toString (); price = price.toString (); const market = this.market (symbol); const request = { 'side': side, 'type': type, 'currency': market['id'].replace (market['quote'], ''), 'amount': amount, 'display': amount, 'price': price, 'instrument': market['id'], }; const response = await this.privatePostWalletsWalletIdOrders (this.extend (request, params)); return { 'info': response, 'id': response['id'], }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const walletIdInParams = ('walletId' in params); if (!walletIdInParams) { throw new ExchangeError (this.id + ' fetchOrder requires a walletId parameter'); } const request = { 'id': id, }; const response = await this.privateGetWalletsWalletIdOrdersId (this.extend (request, params)); return this.parseOrder (response); } async cancelOrder (id, symbol = undefined, params = {}) { const walletIdInParams = ('walletId' in params); if (!walletIdInParams) { throw new ExchangeError (this.id + ' cancelOrder requires a walletId parameter'); } const request = { 'id': id, }; return await this.privateDeleteWalletsWalletIdOrdersId (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (method === 'GET' && Object.keys (query).length) { url += '?' + this.urlencode (query); } if (method === 'POST' && Object.keys (query).length) { body = this.json (query); } if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const timestamp = nonce; const authBody = (method === 'POST') ? body : ''; const auth = [ method, url, authBody, nonce, timestamp ]; const message = nonce + this.json (auth).replace ('\\/', '/'); const hash = this.hash (this.encode (message), 'sha256', 'binary'); const binaryUrl = this.stringToBinary (this.encode (url)); const binhash = this.binaryConcat (binaryUrl, hash); const signature = this.hmac (binhash, this.encode (this.secret), 'sha512', 'base64'); headers = { 'Authorization': this.apiKey + ':' + this.decode (signature), 'Content-Type': 'application/json', 'X-Auth-Timestamp': timestamp, 'X-Auth-Nonce': nonce, }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('code' in response) { throw new ExchangeError (this.id + ' ' + this.json (response)); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],99:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ArgumentsRequired, InvalidOrder, OrderNotFound } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class kkex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'kkex', 'name': 'KKEX', 'countries': [ 'CN', 'US', 'JP' ], 'version': 'v2', 'has': { 'CORS': false, 'fetchBalance': true, 'fetchTickers': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'createMarketOrder': true, 'fetchOrder': true, }, 'timeframes': { '1m': '1min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '4h': '4hour', '12h': '12hour', '1d': '1day', '1w': '1week', '1M': '1month', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/47401462-2e59f800-d74a-11e8-814f-e4ae17b4968a.jpg', 'api': { 'public': 'https://kkex.com/api/v1', 'private': 'https://kkex.com/api/v2', 'v1': 'https://kkex.com/api/v1', }, 'www': 'https://kkex.com', 'doc': 'https://kkex.com/api_wiki/cn/', 'fees': 'https://intercom.help/kkex/fee', }, 'api': { 'public': { 'get': [ 'exchange_rate', 'products', 'assets', 'tickers', 'ticker', 'depth', 'trades', 'kline', ], }, 'private': { 'post': [ 'profile', 'trade', 'batch_trade', 'cancel_order', 'cancel_all_orders', 'order_history', 'userinfo', 'order_info', 'orders_info', ], }, 'v1': { 'post': [ 'process_strategy', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.002, 'maker': 0.002, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, 'options': { 'lastNonceTimestamp': 0, }, }); } async fetchMarkets (params = {}) { let tickers = await this.publicGetTickers (params); tickers = tickers['tickers']; let products = await this.publicGetProducts (params); products = products['products']; const markets = []; for (let k = 0; k < tickers.length; k++) { const keys = Object.keys (tickers[k]); markets.push (keys[0]); } const result = []; for (let i = 0; i < markets.length; i++) { const id = markets[i]; const market = markets[i]; let baseId = ''; let quoteId = ''; let precision = {}; let limits = {}; for (let j = 0; j < products.length; j++) { const p = products[j]; if (p['mark_asset'] + p['base_asset'] === market) { quoteId = p['base_asset']; baseId = p['mark_asset']; const price_scale_str = p['price_scale'].toString (); const scale = price_scale_str.length - 1; precision = { 'price': scale, 'amount': scale, }; limits = { 'amount': { 'min': Math.max (this.safeFloat (p, 'min_bid_size'), this.safeFloat (p, 'min_ask_size')), 'max': Math.min (this.safeFloat (p, 'max_bid_size'), this.safeFloat (p, 'max_ask_size')), }, 'price': { 'min': this.safeFloat (p, 'min_price'), 'max': this.safeFloat (p, 'max_price'), }, }; limits['cost'] = { 'min': this.safeFloat (p, 'min_bid_amount'), 'max': this.safeFloat (p, 'max_bid_amount'), }; } } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } parseTicker (ticker, market = undefined) { const timestamp = this.safeTimestamp (ticker, 'date'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.markets[symbol]; const request = { 'symbol': market['id'], }; const response = await this.publicGetTicker (this.extend (request, params)); const ticker = this.extend (response['ticker'], this.omit (response, 'ticker')); return this.parseTicker (ticker, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickers (params); // // { date: 1540350657, // tickers: [ { ENUBTC: { sell: "0.00000256", // buy: "0.00000253", // last: "0.00000253", // vol: "138686.828804", // high: "0.00000278", // low: "0.00000253", // open: "0.0000027" } }, // { ENUEOS: { sell: "0.00335", // buy: "0.002702", // last: "0.0034", // vol: "15084.9", // high: "0.0034", // low: "0.003189", // open: "0.003189" } } ], // result: true } // const tickers = this.safeValue (response, 'tickers'); const result = {}; for (let i = 0; i < tickers.length; i++) { const ids = Object.keys (tickers[i]); const id = ids[0]; const market = this.safeValue (this.markets_by_id, id); if (market !== undefined) { const symbol = market['symbol']; const ticker = this.extend (tickers[i][id], this.omit (response, 'tickers')); result[symbol] = this.parseTicker (ticker, market); } } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; if (limit !== undefined) { request['size'] = limit; } const response = await this.publicGetDepth (this.extend (request, params)); return this.parseOrderBook (response); } parseTrade (trade, market = undefined) { const timestamp = this.safeInteger (trade, 'date_ms'); const datetime = this.iso8601 (timestamp); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (trade, 'tid'); const type = undefined; const side = this.safeString (trade, 'type'); return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': datetime, 'symbol': symbol, 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostUserinfo (params); const balances = this.safeValue (response, 'info'); const result = { 'info': response }; const funds = this.safeValue (balances, 'funds'); const free = this.safeValue (funds, 'free', {}); const freezed = this.safeValue (funds, 'freezed', {}); const currencyIds = Object.keys (free); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (free, currencyId); account['used'] = this.safeFloat (freezed, currencyId); result[code] = account; } return this.parseBalance (result); } async fetchOrder (id, symbol = undefined, params = {}) { if (!symbol) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_id': id, 'symbol': market['id'], }; const response = await this.privatePostOrderInfo (this.extend (request, params)); if (response['result']) { return this.parseOrder (response['order'], market); } throw new OrderNotFound (this.id + ' order ' + id + ' not found'); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ parseInt (ohlcv[0]), parseFloat (ohlcv[1]), parseFloat (ohlcv[2]), parseFloat (ohlcv[3]), parseFloat (ohlcv[4]), parseFloat (ohlcv[5]), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'type': this.timeframes[timeframe], }; if (since !== undefined) { // since = this.milliseconds () - this.parseTimeframe (timeframe) * limit * 1000; request['since'] = parseInt (since / 1000); } if (limit !== undefined) { request['size'] = limit; } const response = await this.publicGetKline (this.extend (request, params)); // // [ // [ // "1521072000000", // "0.000002", // "0.00003", // "0.000002", // "0.00003", // "3.106889" // ], // [ // "1517356800000", // "0.1", // "0.1", // "0.00000013", // "0.000001", // "542832.83114" // ] // ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } parseOrderStatus (status) { const statuses = { '-1': 'canceled', '0': 'open', '1': 'open', '2': 'closed', '3': 'open', '4': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "status": 2, // "source": "NORMAL", // "amount": "10.852019", // "create_date": 1523938461036, // "avg_price": "0.00096104", // "order_id": "100", // "price": "0.00096105", // "type": "buy", // "symbol": "READBTC", // "deal_amount": "10.852019" // } // let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } let side = this.safeString (order, 'side'); if (side === undefined) { side = this.safeString (order, 'type'); } const timestamp = this.safeInteger (order, 'create_date'); const id = this.safeString2 (order, 'order_id', 'id'); const status = this.parseOrderStatus (this.safeString (order, 'status')); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'deal_amount'); let average = this.safeFloat (order, 'avg_price'); average = this.safeFloat (order, 'price_avg', average); let remaining = undefined; let cost = undefined; if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; } if (average !== undefined) { cost = average * filled; } } return { 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'average': average, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'fee': undefined, 'info': order, 'trades': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'type': side, }; if (type === 'market') { // for market buy it requires the amount of quote currency to spend if (side === 'buy') { if (this.options['createMarketBuyOrderRequiresPrice']) { if (price === undefined) { throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); } else { request['amount'] = this.costToPrecision (symbol, parseFloat (amount) * parseFloat (price)); } } request['price'] = this.costToPrecision (symbol, amount); } else { request['amount'] = this.amountToPrecision (symbol, amount); } request['type'] += '_' + type; } else { request['amount'] = this.amountToPrecision (symbol, amount); request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostTrade (this.extend (request, params)); const id = this.safeString (response, 'order_id'); return { 'info': response, 'id': id, 'datetime': undefined, 'timestamp': undefined, 'lastTradeTimestamp': undefined, 'status': 'open', 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': undefined, 'amount': amount, 'filled': undefined, 'remaining': undefined, 'trades': undefined, 'fee': undefined, 'clientOrderId': undefined, 'average': undefined, }; } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'order_id': id, 'symbol': market['id'], }; return await this.privatePostCancelOrder (this.extend (request, params)); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['page_length'] = limit; // 20 by default } const response = await this.privatePostOrderHistory (this.extend (request, params)); return this.parseOrders (response['orders'], market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': 0, }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': 1, }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + path; if (api === 'public') { url += '?' + this.urlencode (params); headers = { 'Content-Type': 'application/json' }; } else { this.checkRequiredCredentials (); const nonce = this.nonce (); let signature = this.extend ({ 'nonce': nonce, 'api_key': this.apiKey, }, params); signature = this.urlencode (this.keysort (signature)); signature += '&secret_key=' + this.secret; signature = this.hash (this.encode (signature), 'md5'); signature = signature.toUpperCase (); body = this.extend ({ 'api_key': this.apiKey, 'sign': signature, 'nonce': nonce, }, params); body = this.urlencode (body); headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],100:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadSymbol, ExchangeNotAvailable, ArgumentsRequired, PermissionDenied, AuthenticationError, ExchangeError, OrderNotFound, DDoSProtection, InvalidNonce, InsufficientFunds, CancelPending, InvalidOrder, InvalidAddress, NotSupported } = require ('./base/errors'); const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class kraken extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'kraken', 'name': 'Kraken', 'countries': [ 'US' ], 'version': '0', 'rateLimit': 3000, 'certified': true, 'pro': true, 'has': { 'createDepositAddress': true, 'fetchDepositAddress': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'CORS': false, 'fetchCurrencies': true, 'fetchTickers': true, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchWithdrawals': true, 'fetchDeposits': true, 'withdraw': true, 'fetchLedgerEntry': true, 'fetchLedger': true, 'fetchOrderTrades': 'emulated', }, 'marketsByAltname': {}, 'timeframes': { '1m': 1, '5m': 5, '15m': 15, '30m': 30, '1h': 60, '4h': 240, '1d': 1440, '1w': 10080, '2w': 21600, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/51840849/76173629-fc67fb00-61b1-11ea-84fe-f2de582f58a3.jpg', 'api': { 'public': 'https://api.kraken.com', 'private': 'https://api.kraken.com', 'zendesk': 'https://kraken.zendesk.com/api/v2/help_center/en-us/articles', // use the public zendesk api to receive article bodies and bypass new anti-spam protections }, 'www': 'https://www.kraken.com', 'doc': 'https://www.kraken.com/features/api', 'fees': 'https://www.kraken.com/en-us/features/fee-schedule', }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': 0.26 / 100, 'maker': 0.16 / 100, 'tiers': { 'taker': [ [0, 0.0026], [50000, 0.0024], [100000, 0.0022], [250000, 0.0020], [500000, 0.0018], [1000000, 0.0016], [2500000, 0.0014], [5000000, 0.0012], [10000000, 0.0001], ], 'maker': [ [0, 0.0016], [50000, 0.0014], [100000, 0.0012], [250000, 0.0010], [500000, 0.0008], [1000000, 0.0006], [2500000, 0.0004], [5000000, 0.0002], [10000000, 0.0], ], }, }, // this is a bad way of hardcoding fees that change on daily basis // hardcoding is now considered obsolete, we will remove all of it eventually 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BTC': 0.001, 'ETH': 0.005, 'XRP': 0.02, 'XLM': 0.00002, 'LTC': 0.02, 'DOGE': 2, 'ZEC': 0.00010, 'ICN': 0.02, 'REP': 0.01, 'ETC': 0.005, 'MLN': 0.003, 'XMR': 0.05, 'DASH': 0.005, 'GNO': 0.01, 'EOS': 0.5, 'BCH': 0.001, 'XTZ': 0.05, 'USD': 5, // if domestic wire 'EUR': 5, // if domestic wire 'CAD': 10, // CAD EFT Withdrawal 'JPY': 300, // if domestic wire }, 'deposit': { 'BTC': 0, 'ETH': 0, 'XRP': 0, 'XLM': 0, 'LTC': 0, 'DOGE': 0, 'ZEC': 0, 'ICN': 0, 'REP': 0, 'ETC': 0, 'MLN': 0, 'XMR': 0, 'DASH': 0, 'GNO': 0, 'EOS': 0, 'BCH': 0, 'XTZ': 0.05, 'USD': 5, // if domestic wire 'EUR': 0, // free deposit if EUR SEPA Deposit 'CAD': 5, // if domestic wire 'JPY': 0, // Domestic Deposit (Free, ¥5,000 deposit minimum) }, }, }, 'api': { 'zendesk': { 'get': [ // we should really refrain from putting fixed fee numbers and stop hardcoding // we will be using their web APIs to scrape all numbers from these articles '205893708', // -What-is-the-minimum-order-size- '360000292886', // -What-are-the-deposit-fees- '201893608', // -What-are-the-withdrawal-fees- ], }, 'public': { 'get': [ 'Assets', 'AssetPairs', 'Depth', 'OHLC', 'Spread', 'Ticker', 'Time', 'Trades', ], }, 'private': { 'post': [ 'AddOrder', 'AddExport', 'Balance', 'CancelOrder', 'ClosedOrders', 'DepositAddresses', 'DepositMethods', 'DepositStatus', 'ExportStatus', 'GetWebSocketsToken', 'Ledgers', 'OpenOrders', 'OpenPositions', 'QueryLedgers', 'QueryOrders', 'QueryTrades', 'RetrieveExport', 'RemoveExport', 'TradeBalance', 'TradesHistory', 'TradeVolume', 'Withdraw', 'WithdrawCancel', 'WithdrawInfo', 'WithdrawStatus', ], }, }, 'commonCurrencies': { 'XBT': 'BTC', 'XDG': 'DOGE', }, 'options': { 'cacheDepositMethodsOnFetchDepositAddress': true, // will issue up to two calls in fetchDepositAddress 'depositMethods': {}, 'delistedMarketsById': {}, // cannot withdraw/deposit these 'inactiveCurrencies': [ 'CAD', 'USD', 'JPY', 'GBP' ], 'fetchMinOrderAmounts': true, }, 'exceptions': { 'EQuery:Invalid asset pair': BadSymbol, // {"error":["EQuery:Invalid asset pair"]} 'EAPI:Invalid key': AuthenticationError, 'EFunding:Unknown withdraw key': ExchangeError, 'EFunding:Invalid amount': InsufficientFunds, 'EService:Unavailable': ExchangeNotAvailable, 'EDatabase:Internal error': ExchangeNotAvailable, 'EService:Busy': ExchangeNotAvailable, 'EQuery:Unknown asset': ExchangeError, 'EAPI:Rate limit exceeded': DDoSProtection, 'EOrder:Rate limit exceeded': DDoSProtection, 'EGeneral:Internal error': ExchangeNotAvailable, 'EGeneral:Temporary lockout': DDoSProtection, 'EGeneral:Permission denied': PermissionDenied, }, }); } costToPrecision (symbol, cost) { return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); } feeToPrecision (symbol, fee) { return this.decimalToPrecision (fee, TRUNCATE, this.markets[symbol]['precision']['amount'], DECIMAL_PLACES); } async fetchMinOrderAmounts (params = {}) { const response = await this.zendeskGet205893708 (params); const article = this.safeValue (response, 'article'); const html = this.safeString (article, 'body'); const parts = html.split (''); const numParts = parts.length; if (numParts < 3) { throw new NotSupported (this.id + ' fetchMinOrderAmounts HTML page markup has changed: https://kraken.zendesk.com/api/v2/help_center/en-us/articles/205893708'); } const result = {}; // skip the part before the header and the header itself for (let i = 2; i < parts.length; i++) { const part = parts[i]; const chunks = part.split (''); const amountAndCode = chunks[0]; if (amountAndCode !== 'To Be Announced') { const pieces = amountAndCode.split (' '); const numPieces = pieces.length; if (numPieces === 2) { const amount = parseFloat (pieces[0]); const code = this.safeCurrencyCode (pieces[1]); result[code] = amount; } } } return result; } async fetchMarkets (params = {}) { const response = await this.publicGetAssetPairs (params); // // { // "error":[], // "result":{ // "ADAETH":{ // "altname":"ADAETH", // "wsname":"ADA\/ETH", // "aclass_base":"currency", // "base":"ADA", // "aclass_quote":"currency", // "quote":"XETH", // "lot":"unit", // "pair_decimals":7, // "lot_decimals":8, // "lot_multiplier":1, // "leverage_buy":[], // "leverage_sell":[], // "fees":[ // [0,0.26], // [50000,0.24], // [100000,0.22], // [250000,0.2], // [500000,0.18], // [1000000,0.16], // [2500000,0.14], // [5000000,0.12], // [10000000,0.1] // ], // "fees_maker":[ // [0,0.16], // [50000,0.14], // [100000,0.12], // [250000,0.1], // [500000,0.08], // [1000000,0.06], // [2500000,0.04], // [5000000,0.02], // [10000000,0] // ], // "fee_volume_currency":"ZUSD", // "margin_call":80, // "margin_stop":40 // }, // } // } // const fetchMinOrderAmounts = this.safeValue (this.options, 'fetchMinOrderAmounts', false); let limits = {}; if (fetchMinOrderAmounts) { limits = await this.fetchMinOrderAmounts (); } const keys = Object.keys (response['result']); let result = []; for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = response['result'][id]; const baseId = market['base']; const quoteId = market['quote']; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const darkpool = id.indexOf ('.d') >= 0; const symbol = darkpool ? market['altname'] : (base + '/' + quote); let maker = undefined; if ('fees_maker' in market) { maker = parseFloat (market['fees_maker'][0][1]) / 100; } const precision = { 'amount': market['lot_decimals'], 'price': market['pair_decimals'], }; let minAmount = Math.pow (10, -precision['amount']); if (base in limits) { minAmount = limits[base]; } result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'darkpool': darkpool, 'info': market, 'altname': market['altname'], 'maker': maker, 'taker': parseFloat (market['fees'][0][1]) / 100, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': minAmount, 'max': Math.pow (10, precision['amount']), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': 0, 'max': undefined, }, }, }); } result = this.appendInactiveMarkets (result); this.marketsByAltname = this.indexBy (result, 'altname'); return result; } safeCurrencyCode (currencyId, currency = undefined) { if (currencyId.length > 3) { if ((currencyId.indexOf ('X') === 0) || (currencyId.indexOf ('Z') === 0)) { currencyId = currencyId.slice (1); } } return super.safeCurrencyCode (currencyId, currency); } appendInactiveMarkets (result) { // result should be an array to append to const precision = { 'amount': 8, 'price': 8 }; const costLimits = { 'min': 0, 'max': undefined }; const priceLimits = { 'min': Math.pow (10, -precision['price']), 'max': undefined }; const amountLimits = { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']) }; const limits = { 'amount': amountLimits, 'price': priceLimits, 'cost': costLimits }; const defaults = { 'darkpool': false, 'info': undefined, 'maker': undefined, 'taker': undefined, 'active': false, 'precision': precision, 'limits': limits, }; const markets = [ // { 'id': 'XXLMZEUR', 'symbol': 'XLM/EUR', 'base': 'XLM', 'quote': 'EUR', 'altname': 'XLMEUR' }, ]; for (let i = 0; i < markets.length; i++) { result.push (this.extend (defaults, markets[i])); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetAssets (params); // // { // "error": [], // "result": { // "ADA": { "aclass": "currency", "altname": "ADA", "decimals": 8, "display_decimals": 6 }, // "BCH": { "aclass": "currency", "altname": "BCH", "decimals": 10, "display_decimals": 5 }, // ... // }, // } // const currencies = this.safeValue (response, 'result'); const ids = Object.keys (currencies); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const currency = currencies[id]; // todo: will need to rethink the fees // see: https://support.kraken.com/hc/en-us/articles/201893608-What-are-the-withdrawal-fees- // to add support for multiple withdrawal/deposit methods and // differentiated fees for each particular method const code = this.safeCurrencyCode (this.safeString (currency, 'altname')); const precision = this.safeInteger (currency, 'decimals'); // assumes all currencies are active except those listed above const active = !this.inArray (code, this.options['inactiveCurrencies']); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': code, 'active': active, 'fee': undefined, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': Math.pow (10, precision), }, }, }; } return result; } async fetchTradingFees (params = {}) { await this.loadMarkets (); this.checkRequiredCredentials (); const response = await this.privatePostTradeVolume (params); const tradedVolume = this.safeFloat (response['result'], 'volume'); const tiers = this.fees['trading']['tiers']; let taker = tiers['taker'][1]; let maker = tiers['maker'][1]; for (let i = 0; i < tiers['taker'].length; i++) { if (tradedVolume >= tiers['taker'][i][0]) { taker = tiers['taker'][i][1]; } } for (let i = 0; i < tiers['maker'].length; i++) { if (tradedVolume >= tiers['maker'][i][0]) { maker = tiers['maker'][i][1]; } } return { 'info': response, 'maker': maker, 'taker': taker, }; } parseBidAsk (bidask, priceKey = 0, amountKey = 1) { const price = this.safeFloat (bidask, priceKey); const amount = this.safeFloat (bidask, amountKey); const timestamp = this.safeInteger (bidask, 2); return [ price, amount, timestamp ]; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (market['darkpool']) { throw new ExchangeError (this.id + ' does not provide an order book for darkpool symbol ' + symbol); } const request = { 'pair': market['id'], }; if (limit !== undefined) { request['count'] = limit; // 100 } const response = await this.publicGetDepth (this.extend (request, params)); // // { // "error":[], // "result":{ // "XETHXXBT":{ // "asks":[ // ["0.023480","4.000",1586321307], // ["0.023490","50.095",1586321306], // ["0.023500","28.535",1586321302], // ], // "bids":[ // ["0.023470","59.580",1586321307], // ["0.023460","20.000",1586321301], // ["0.023440","67.832",1586321306], // ] // } // } // } // const result = this.safeValue (response, 'result', {}); const orderbook = this.safeValue (result, market['id']); return this.parseOrderBook (orderbook); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market) { symbol = market['symbol']; } const baseVolume = parseFloat (ticker['v'][1]); const vwap = parseFloat (ticker['p'][1]); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = parseFloat (ticker['c'][0]); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': parseFloat (ticker['h'][1]), 'low': parseFloat (ticker['l'][1]), 'bid': parseFloat (ticker['b'][0]), 'bidVolume': undefined, 'ask': parseFloat (ticker['a'][0]), 'askVolume': undefined, 'vwap': vwap, 'open': this.safeFloat (ticker, 'o'), 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); symbols = (symbols === undefined) ? this.symbols : symbols; const marketIds = []; for (let i = 0; i < this.symbols.length; i++) { const symbol = this.symbols[i]; const market = this.markets[symbol]; if (market['active'] && !market['darkpool']) { marketIds.push (market['id']); } } const request = { 'pair': marketIds.join (','), }; const response = await this.publicGetTicker (this.extend (request, params)); const tickers = response['result']; const ids = Object.keys (tickers); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = this.markets_by_id[id]; const symbol = market['symbol']; const ticker = tickers[id]; if (this.inArray (symbol, symbols)) { result[symbol] = this.parseTicker (ticker, market); } } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const darkpool = symbol.indexOf ('.d') >= 0; if (darkpool) { throw new ExchangeError (this.id + ' does not provide a ticker for darkpool symbol ' + symbol); } const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetTicker (this.extend (request, params)); const ticker = response['result'][market['id']]; return this.parseTicker (ticker, market); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, parseFloat (ohlcv[1]), parseFloat (ohlcv[2]), parseFloat (ohlcv[3]), parseFloat (ohlcv[4]), parseFloat (ohlcv[6]), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'interval': this.timeframes[timeframe], }; if (since !== undefined) { request['since'] = parseInt ((since - 1) / 1000); } const response = await this.publicGetOHLC (this.extend (request, params)); const ohlcvs = response['result'][market['id']]; return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } parseLedgerEntryType (type) { const types = { 'trade': 'trade', 'withdrawal': 'transaction', 'deposit': 'transaction', 'transfer': 'transfer', 'margin': 'margin', }; return this.safeString (types, type, type); } parseLedgerEntry (item, currency = undefined) { // // { // 'LTFK7F-N2CUX-PNY4SX': { // refid: "TSJTGT-DT7WN-GPPQMJ", // time: 1520102320.555, // type: "trade", // aclass: "currency", // asset: "XETH", // amount: "0.1087194600", // fee: "0.0000000000", // balance: "0.2855851000" // }, // ... // } // const id = this.safeString (item, 'id'); let direction = undefined; const account = undefined; const referenceId = this.safeString (item, 'refid'); const referenceAccount = undefined; const type = this.parseLedgerEntryType (this.safeString (item, 'type')); const code = this.safeCurrencyCode (this.safeString (item, 'asset'), currency); let amount = this.safeFloat (item, 'amount'); if (amount < 0) { direction = 'out'; amount = Math.abs (amount); } else { direction = 'in'; } const time = this.safeFloat (item, 'time'); let timestamp = undefined; if (time !== undefined) { timestamp = parseInt (time * 1000); } const fee = { 'cost': this.safeFloat (item, 'fee'), 'currency': code, }; const before = undefined; const after = this.safeFloat (item, 'balance'); const status = 'ok'; return { 'info': item, 'id': id, 'direction': direction, 'account': account, 'referenceId': referenceId, 'referenceAccount': referenceAccount, 'type': type, 'currency': code, 'amount': amount, 'before': before, 'after': after, 'status': status, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': fee, }; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { // https://www.kraken.com/features/api#get-ledgers-info await this.loadMarkets (); const request = {}; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['asset'] = currency['id']; } if (since !== undefined) { request['start'] = parseInt (since / 1000); } const response = await this.privatePostLedgers (this.extend (request, params)); // { error: [], // result: { ledger: { 'LPUAIB-TS774-UKHP7X': { refid: "A2B4HBV-L4MDIE-JU4N3N", // time: 1520103488.314, // type: "withdrawal", // aclass: "currency", // asset: "XETH", // amount: "-0.2805800000", // fee: "0.0050000000", // balance: "0.0000051000" }, const result = this.safeValue (response, 'result', {}); const ledger = this.safeValue (result, 'ledger', {}); const keys = Object.keys (ledger); const items = []; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = ledger[key]; value['id'] = key; items.push (value); } return this.parseLedger (items, currency, since, limit); } async fetchLedgerEntriesByIds (ids, code = undefined, params = {}) { // https://www.kraken.com/features/api#query-ledgers await this.loadMarkets (); ids = ids.join (','); const request = this.extend ({ 'id': ids, }, params); const response = await this.privatePostQueryLedgers (request); // { error: [], // result: { 'LPUAIB-TS774-UKHP7X': { refid: "A2B4HBV-L4MDIE-JU4N3N", // time: 1520103488.314, // type: "withdrawal", // aclass: "currency", // asset: "XETH", // amount: "-0.2805800000", // fee: "0.0050000000", // balance: "0.0000051000" } } } const result = response['result']; const keys = Object.keys (result); const items = []; for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = result[key]; value['id'] = key; items.push (value); } return this.parseLedger (items); } async fetchLedgerEntry (id, code = undefined, params = {}) { const items = await this.fetchLedgerEntriesByIds ([ id ], code, params); return items[0]; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // [ // "0.032310", // price // "4.28169434", // amount // 1541390792.763, // timestamp // "s", // sell or buy // "l", // limit or market // "" // ] // // fetchOrderTrades (private) // // { // id: 'TIMIRG-WUNNE-RRJ6GT', // injected from outside // ordertxid: 'OQRPN2-LRHFY-HIFA7D', // postxid: 'TKH2SE-M7IF5-CFI7LT', // pair: 'USDCUSDT', // time: 1586340086.457, // type: 'sell', // ordertype: 'market', // price: '0.99860000', // cost: '22.16892001', // fee: '0.04433784', // vol: '22.20000000', // margin: '0.00000000', // misc: '' // } // let timestamp = undefined; let side = undefined; let type = undefined; let price = undefined; let amount = undefined; let cost = undefined; let id = undefined; let order = undefined; let fee = undefined; let symbol = undefined; if (Array.isArray (trade)) { timestamp = this.safeTimestamp (trade, 2); side = (trade[3] === 's') ? 'sell' : 'buy'; type = (trade[4] === 'l') ? 'limit' : 'market'; price = this.safeFloat (trade, 0); amount = this.safeFloat (trade, 1); const tradeLength = trade.length; if (tradeLength > 6) { id = this.safeString (trade, 6); // artificially added as per #1794 } } else if (typeof trade === 'string') { id = trade; } else if ('ordertxid' in trade) { const marketId = this.safeString (trade, 'pair'); const foundMarket = this.findMarketByAltnameOrId (marketId); if (foundMarket !== undefined) { market = foundMarket; } else if (marketId !== undefined) { // delisted market ids go here market = this.getDelistedMarketById (marketId); } order = trade['ordertxid']; id = this.safeString2 (trade, 'id', 'postxid'); timestamp = this.safeTimestamp (trade, 'time'); side = this.safeString (trade, 'type'); type = this.safeString (trade, 'ordertype'); price = this.safeFloat (trade, 'price'); amount = this.safeFloat (trade, 'vol'); if ('fee' in trade) { let currency = undefined; if (market !== undefined) { currency = market['quote']; } fee = { 'cost': this.safeFloat (trade, 'fee'), 'currency': currency, }; } } if (market !== undefined) { symbol = market['symbol']; } if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } return { 'id': id, 'order': order, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const id = market['id']; const request = { 'pair': id, }; // https://support.kraken.com/hc/en-us/articles/218198197-How-to-pull-all-trade-data-using-the-Kraken-REST-API // https://github.com/ccxt/ccxt/issues/5677 if (since !== undefined) { // php does not format it properly // therefore we use string concatenation here request['since'] = since * 1e6; request['since'] = since.toString () + '000000'; // expected to be in nanoseconds } // https://github.com/ccxt/ccxt/issues/5698 if (limit !== undefined && limit !== 1000) { const fetchTradesWarning = this.safeValue (this.options, 'fetchTradesWarning', true); if (fetchTradesWarning) { throw new ExchangeError (this.id + ' fetchTrades() cannot serve ' + limit.toString () + " trades without breaking the pagination, see https://github.com/ccxt/ccxt/issues/5698 for more details. Set exchange.options['fetchTradesWarning'] to acknowledge this warning and silence it."); } } const response = await this.publicGetTrades (this.extend (request, params)); // // { // "error": [], // "result": { // "XETHXXBT": [ // ["0.032310","4.28169434",1541390792.763,"s","l",""] // ], // "last": "1541439421200678657" // } // } // const result = response['result']; const trades = result[id]; // trades is a sorted array: last (most recent trade) goes last const length = trades.length; if (length <= 0) { return []; } const lastTrade = trades[length - 1]; const lastTradeId = this.safeString (result, 'last'); lastTrade.push (lastTradeId); return this.parseTrades (trades, market, since, limit); } async fetchBalance (params = {}) { const response = await this.privatePostBalance (params); const balances = this.safeValue (response, 'result', {}); const result = { 'info': balances }; const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balances, currencyId); result[code] = account; } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'type': side, 'ordertype': type, 'volume': this.amountToPrecision (symbol, amount), }; const priceIsDefined = (price !== undefined); const marketOrder = (type === 'market'); const limitOrder = (type === 'limit'); const shouldIncludePrice = limitOrder || (!marketOrder && priceIsDefined); if (shouldIncludePrice) { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostAddOrder (this.extend (request, params)); let id = this.safeValue (response['result'], 'txid'); if (id !== undefined) { if (Array.isArray (id)) { const length = id.length; id = (length > 1) ? id : id[0]; } } const clientOrderId = this.safeString (params, 'userref'); return { 'id': id, 'clientOrderId': clientOrderId, 'info': response, 'timestamp': undefined, 'datetime': undefined, 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': undefined, 'average': undefined, 'filled': undefined, 'remaining': undefined, 'status': undefined, 'fee': undefined, 'trades': undefined, }; } findMarketByAltnameOrId (id) { if (id in this.marketsByAltname) { return this.marketsByAltname[id]; } else if (id in this.markets_by_id) { return this.markets_by_id[id]; } return undefined; } getDelistedMarketById (id) { if (id === undefined) { return id; } let market = this.safeValue (this.options['delistedMarketsById'], id); if (market !== undefined) { return market; } const baseIdStart = 0; let baseIdEnd = 3; let quoteIdStart = 3; let quoteIdEnd = 6; if (id.length === 8) { baseIdEnd = 4; quoteIdStart = 4; quoteIdEnd = 8; } else if (id.length === 7) { baseIdEnd = 4; quoteIdStart = 4; quoteIdEnd = 7; } const baseId = id.slice (baseIdStart, baseIdEnd); const quoteId = id.slice (quoteIdStart, quoteIdEnd); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; market = { 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, }; this.options['delistedMarketsById'][id] = market; return market; } parseOrderStatus (status) { const statuses = { 'pending': 'open', // order pending book entry 'open': 'open', 'closed': 'closed', 'canceled': 'canceled', 'expired': 'expired', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const description = this.safeValue (order, 'descr', {}); const side = this.safeString (description, 'type'); const type = this.safeString (description, 'ordertype'); const marketId = this.safeString (description, 'pair'); const foundMarket = this.findMarketByAltnameOrId (marketId); let symbol = undefined; if (foundMarket !== undefined) { market = foundMarket; } else if (marketId !== undefined) { // delisted market ids go here market = this.getDelistedMarketById (marketId); } const timestamp = this.safeTimestamp (order, 'opentm'); const amount = this.safeFloat (order, 'vol'); const filled = this.safeFloat (order, 'vol_exec'); const remaining = amount - filled; let fee = undefined; const cost = this.safeFloat (order, 'cost'); let price = this.safeFloat (description, 'price'); if ((price === undefined) || (price === 0)) { price = this.safeFloat (description, 'price2'); } if ((price === undefined) || (price === 0)) { price = this.safeFloat (order, 'price', price); } const average = this.safeFloat (order, 'price'); if (market !== undefined) { symbol = market['symbol']; if ('fee' in order) { const flags = order['oflags']; const feeCost = this.safeFloat (order, 'fee'); fee = { 'cost': feeCost, 'rate': undefined, }; if (flags.indexOf ('fciq') >= 0) { fee['currency'] = market['quote']; } else if (flags.indexOf ('fcib') >= 0) { fee['currency'] = market['base']; } } } const status = this.parseOrderStatus (this.safeString (order, 'status')); const id = this.safeString (order, 'id'); const clientOrderId = this.safeString (order, 'userref'); const rawTrades = this.safeValue (order, 'trades'); let trades = undefined; if (rawTrades !== undefined) { trades = this.parseTrades (rawTrades, market, undefined, undefined, { 'order': id }); } return { 'id': id, 'clientOrderId': clientOrderId, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'average': average, 'remaining': remaining, 'fee': fee, 'trades': trades, }; } parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { const result = []; const ids = Object.keys (orders); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } for (let i = 0; i < ids.length; i++) { const id = ids[i]; const order = this.extend ({ 'id': id }, orders[id]); result.push (this.extend (this.parseOrder (order, market), params)); } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const clientOrderId = this.safeValue2 (params, 'userref', 'clientOrderId'); const request = { 'trades': true, // whether or not to include trades in output (optional, default false) // 'txid': id, // do not comma separate a list of ids - use fetchOrdersByIds instead // 'userref': 'optional', // restrict results to given user reference id (optional) }; let query = params; if (clientOrderId !== undefined) { request['userref'] = clientOrderId; query = this.omit (params, [ 'userref', 'clientOrderId' ]); } else { request['txid'] = id; } const response = await this.privatePostQueryOrders (this.extend (request, query)); // // { // "error":[], // "result":{ // "OTLAS3-RRHUF-NDWH5A":{ // "refid":null, // "userref":null, // "status":"closed", // "reason":null, // "opentm":1586822919.3342, // "closetm":1586822919.365, // "starttm":0, // "expiretm":0, // "descr":{ // "pair":"XBTUSDT", // "type":"sell", // "ordertype":"market", // "price":"0", // "price2":"0", // "leverage":"none", // "order":"sell 0.21804000 XBTUSDT @ market", // "close":"" // }, // "vol":"0.21804000", // "vol_exec":"0.21804000", // "cost":"1493.9", // "fee":"3.8", // "price":"6851.5", // "stopprice":"0.00000", // "limitprice":"0.00000", // "misc":"", // "oflags":"fciq", // "trades":["TT5UC3-GOIRW-6AZZ6R"] // } // } // } // const orders = this.safeValue (response, 'result', []); const order = this.parseOrder (this.extend ({ 'id': id }, orders[id])); return this.extend ({ 'info': response }, order); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { const orderTrades = this.safeValue (params, 'trades'); const tradeIds = []; if (orderTrades === undefined) { throw new ArgumentsRequired (this.id + " fetchOrderTrades requires a unified order structure in the params argument or a 'trades' param (an array of trade id strings)"); } else { for (let i = 0; i < orderTrades.length; i++) { const orderTrade = orderTrades[i]; if (typeof orderTrade === 'string') { tradeIds.push (orderTrade); } else { tradeIds.push (orderTrade['id']); } } } await this.loadMarkets (); const options = this.safeValue (this.options, 'fetchOrderTrades', {}); const batchSize = this.safeInteger (options, 'batchSize', 20); let numBatches = parseInt (tradeIds / batchSize); numBatches = this.sum (numBatches, 1); const numTradeIds = tradeIds.length; let result = []; for (let j = 0; j < numBatches; j++) { const requestIds = []; for (let k = 0; k < batchSize; k++) { const index = this.sum (j * batchSize, k); if (index < numTradeIds) { requestIds.push (tradeIds[index]); } } const request = { 'txid': requestIds.join (','), }; const response = await this.privatePostQueryTrades (request); // // { // error: [], // result: { // 'TIMIRG-WUNNE-RRJ6GT': { // ordertxid: 'OQRPN2-LRHFY-HIFA7D', // postxid: 'TKH2SE-M7IF5-CFI7LT', // pair: 'USDCUSDT', // time: 1586340086.457, // type: 'sell', // ordertype: 'market', // price: '0.99860000', // cost: '22.16892001', // fee: '0.04433784', // vol: '22.20000000', // margin: '0.00000000', // misc: '' // } // } // } // const rawTrades = this.safeValue (response, 'result'); const ids = Object.keys (rawTrades); for (let i = 0; i < ids.length; i++) { rawTrades[ids[i]]['id'] = ids[i]; } const trades = this.parseTrades (rawTrades, undefined, since, limit); const tradesFilteredBySymbol = this.filterBySymbol (trades, symbol); result = this.arrayConcat (result, tradesFilteredBySymbol); } return result; } async fetchOrdersByIds (ids, symbol = undefined, params = {}) { await this.loadMarkets (); const response = await this.privatePostQueryOrders (this.extend ({ 'trades': true, // whether or not to include trades in output (optional, default false) 'txid': ids.join (','), // comma delimited list of transaction ids to query info about (20 maximum) }, params)); const result = this.safeValue (response, 'result', {}); const orders = []; const orderIds = Object.keys (result); for (let i = 0; i < orderIds.length; i++) { const id = orderIds[i]; const item = result[id]; const order = this.parseOrder (this.extend ({ 'id': id }, item)); orders.push (order); } return orders; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'type': 'all', // any position, closed position, closing position, no position // 'trades': false, // whether or not to include trades related to position in output // 'start': 1234567890, // starting unix timestamp or trade tx id of results (exclusive) // 'end': 1234567890, // ending unix timestamp or trade tx id of results (inclusive) // 'ofs' = result offset }; if (since !== undefined) { request['start'] = parseInt (since / 1000); } const response = await this.privatePostTradesHistory (this.extend (request, params)); // // { // "error": [], // "result": { // "trades": { // "GJ3NYQ-XJRTF-THZABF": { // "ordertxid": "TKH2SE-ZIF5E-CFI7LT", // "postxid": "OEN3VX-M7IF5-JNBJAM", // "pair": "XICNXETH", // "time": 1527213229.4491, // "type": "sell", // "ordertype": "limit", // "price": "0.001612", // "cost": "0.025792", // "fee": "0.000026", // "vol": "16.00000000", // "margin": "0.000000", // "misc": "" // }, // ... // }, // "count": 9760, // }, // } // const trades = response['result']['trades']; const ids = Object.keys (trades); for (let i = 0; i < ids.length; i++) { trades[ids[i]]['id'] = ids[i]; } const result = this.parseTrades (trades, undefined, since, limit); if (symbol === undefined) { return result; } return this.filterBySymbol (result, symbol); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); let response = undefined; try { response = await this.privatePostCancelOrder (this.extend ({ 'txid': id, }, params)); } catch (e) { if (this.last_http_response) { if (this.last_http_response.indexOf ('EOrder:Unknown order') >= 0) { throw new OrderNotFound (this.id + ' cancelOrder() error ' + this.last_http_response); } } throw e; } return response; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (since !== undefined) { request['start'] = parseInt (since / 1000); } const response = await this.privatePostOpenOrders (this.extend (request, params)); const orders = this.parseOrders (response['result']['open'], undefined, since, limit); if (symbol === undefined) { return orders; } return this.filterBySymbol (orders, symbol); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (since !== undefined) { request['start'] = parseInt (since / 1000); } const response = await this.privatePostClosedOrders (this.extend (request, params)); const orders = this.parseOrders (response['result']['closed'], undefined, since, limit); if (symbol === undefined) { return orders; } return this.filterBySymbol (orders, symbol); } async fetchDepositMethods (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset': currency['id'], }; const response = await this.privatePostDepositMethods (this.extend (request, params)); return response['result']; } parseTransactionStatus (status) { // IFEX transaction states const statuses = { 'Initial': 'pending', 'Pending': 'pending', 'Success': 'ok', 'Settled': 'pending', 'Failure': 'failed', 'Partial': 'ok', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { method: "Ether (Hex)", // aclass: "currency", // asset: "XETH", // refid: "Q2CANKL-LBFVEE-U4Y2WQ", // txid: "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…", // info: "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290", // amount: "7.9999257900", // fee: "0.0000000000", // time: 1529223212, // status: "Success" } // // fetchWithdrawals // // { method: "Ether", // aclass: "currency", // asset: "XETH", // refid: "A2BF34S-O7LBNQ-UE4Y4O", // txid: "0x288b83c6b0904d8400ef44e1c9e2187b5c8f7ea3d838222d53f701a15b5c274d", // info: "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0", // amount: "9.9950000000", // fee: "0.0050000000", // time: 1530481750, // status: "Success" } // const id = this.safeString (transaction, 'refid'); const txid = this.safeString (transaction, 'txid'); const timestamp = this.safeTimestamp (transaction, 'time'); const currencyId = this.safeString (transaction, 'asset'); const code = this.safeCurrencyCode (currencyId, currency); const address = this.safeString (transaction, 'info'); const amount = this.safeFloat (transaction, 'amount'); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const type = this.safeString (transaction, 'type'); // injected from the outside let feeCost = this.safeFloat (transaction, 'fee'); if (feeCost === undefined) { if (type === 'deposit') { feeCost = 0; } } return { 'info': transaction, 'id': id, 'currency': code, 'amount': amount, 'address': address, 'tag': undefined, 'status': status, 'type': type, 'updated': undefined, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': { 'currency': code, 'cost': feeCost, }, }; } parseTransactionsByType (type, transactions, code = undefined, since = undefined, limit = undefined) { const result = []; for (let i = 0; i < transactions.length; i++) { const transaction = this.parseTransaction (this.extend ({ 'type': type, }, transactions[i])); result.push (transaction); } return this.filterByCurrencySinceLimit (result, code, since, limit); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { // https://www.kraken.com/en-us/help/api#deposit-status if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset': currency['id'], }; const response = await this.privatePostDepositStatus (this.extend (request, params)); // // { error: [], // result: [ { method: "Ether (Hex)", // aclass: "currency", // asset: "XETH", // refid: "Q2CANKL-LBFVEE-U4Y2WQ", // txid: "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…", // info: "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290", // amount: "7.9999257900", // fee: "0.0000000000", // time: 1529223212, // status: "Success" } ] } // return this.parseTransactionsByType ('deposit', response['result'], code, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { // https://www.kraken.com/en-us/help/api#withdraw-status if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset': currency['id'], }; const response = await this.privatePostWithdrawStatus (this.extend (request, params)); // // { error: [], // result: [ { method: "Ether", // aclass: "currency", // asset: "XETH", // refid: "A2BF34S-O7LBNQ-UE4Y4O", // txid: "0x298c83c7b0904d8400ef43e1c9e2287b518f7ea3d838822d53f704a1565c274d", // info: "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0", // amount: "9.9950000000", // fee: "0.0050000000", // time: 1530481750, // status: "Success" } ] } // return this.parseTransactionsByType ('withdrawal', response['result'], code, since, limit); } async createDepositAddress (code, params = {}) { const request = { 'new': 'true', }; const response = await this.fetchDepositAddress (code, this.extend (request, params)); const address = this.safeString (response, 'address'); this.checkAddress (address); return { 'currency': code, 'address': address, 'info': response, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); // eslint-disable-next-line quotes let method = this.safeString (params, 'method'); if (method === undefined) { if (this.options['cacheDepositMethodsOnFetchDepositAddress']) { // cache depositMethods if (!(code in this.options['depositMethods'])) { this.options['depositMethods'][code] = await this.fetchDepositMethods (code); } method = this.options['depositMethods'][code][0]['method']; } else { throw new ArgumentsRequired (this.id + ' fetchDepositAddress() requires an extra `method` parameter. Use fetchDepositMethods ("' + code + '") to get a list of available deposit methods or enable the exchange property .options["cacheDepositMethodsOnFetchDepositAddress"] = true'); } } const request = { 'asset': currency['id'], 'method': method, }; const response = await this.privatePostDepositAddresses (this.extend (request, params)); // overwrite methods const result = response['result']; const numResults = result.length; if (numResults < 1) { throw new InvalidAddress (this.id + ' privatePostDepositAddresses() returned no addresses'); } const address = this.safeString (result[0], 'address'); const tag = this.safeString2 (result[0], 'tag', 'memo'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); if ('key' in params) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'asset': currency['id'], 'amount': amount, // 'address': address, // they don't allow withdrawals to direct addresses }; const response = await this.privatePostWithdraw (this.extend (request, params)); return { 'info': response, 'id': response['result'], }; } throw new ExchangeError (this.id + " withdraw requires a 'key' parameter (withdrawal key name, as set up on your account)"); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = '/' + this.version + '/' + api + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); body = this.urlencode (this.extend ({ 'nonce': nonce }, params)); const auth = this.encode (nonce + body); const hash = this.hash (auth, 'sha256', 'binary'); const binary = this.stringToBinary (this.encode (url)); const binhash = this.binaryConcat (binary, hash); const secret = this.base64ToBinary (this.secret); const signature = this.hmac (binhash, secret, 'sha512', 'base64'); headers = { 'API-Key': this.apiKey, 'API-Sign': this.decode (signature), 'Content-Type': 'application/x-www-form-urlencoded', }; } else { url = '/' + path; } url = this.urls['api'][api] + url; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } nonce () { return this.milliseconds (); } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (code === 520) { throw new ExchangeNotAvailable (this.id + ' ' + code.toString () + ' ' + reason); } // todo: rewrite this for "broad" exceptions matching if (body.indexOf ('Invalid order') >= 0) { throw new InvalidOrder (this.id + ' ' + body); } if (body.indexOf ('Invalid nonce') >= 0) { throw new InvalidNonce (this.id + ' ' + body); } if (body.indexOf ('Insufficient funds') >= 0) { throw new InsufficientFunds (this.id + ' ' + body); } if (body.indexOf ('Cancel pending') >= 0) { throw new CancelPending (this.id + ' ' + body); } if (body.indexOf ('Invalid arguments:volume') >= 0) { throw new InvalidOrder (this.id + ' ' + body); } if (body[0] === '{') { if (typeof response !== 'string') { if ('error' in response) { const numErrors = response['error'].length; if (numErrors) { const message = this.id + ' ' + body; for (let i = 0; i < response['error'].length; i++) { const error = response['error'][i]; this.throwExactlyMatchedException (this.exceptions, error, message); } throw new ExchangeError (message); } } } } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],101:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, AccountSuspended, InvalidNonce, NotSupported, BadRequest, AuthenticationError, BadSymbol, RateLimitExceeded } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class kucoin extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'kucoin', 'name': 'KuCoin', 'countries': [ 'SC' ], 'rateLimit': 334, 'version': 'v2', 'certified': false, 'pro': true, 'comment': 'Platform 2.0', 'has': { 'CORS': false, 'fetchTime': true, 'fetchMarkets': true, 'fetchCurrencies': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchOrderBook': true, 'fetchOrder': true, 'fetchClosedOrders': true, 'fetchOpenOrders': true, 'fetchDepositAddress': true, 'createDepositAddress': true, 'withdraw': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchBalance': true, 'fetchTrades': true, 'fetchMyTrades': true, 'createOrder': true, 'cancelOrder': true, 'fetchAccounts': true, 'fetchFundingFee': true, 'fetchOHLCV': true, 'fetchLedger': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/57369448-3cc3aa80-7196-11e9-883e-5ebeb35e4f57.jpg', 'referral': 'https://www.kucoin.com/?rcode=E5wkqe', 'api': { 'public': 'https://openapi-v2.kucoin.com', 'private': 'https://openapi-v2.kucoin.com', }, 'test': { 'public': 'https://openapi-sandbox.kucoin.com', 'private': 'https://openapi-sandbox.kucoin.com', }, 'www': 'https://www.kucoin.com', 'doc': [ 'https://docs.kucoin.com', ], }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'password': true, }, 'api': { 'public': { 'get': [ 'timestamp', 'status', 'symbols', 'markets', 'market/allTickers', 'market/orderbook/level{level}', 'market/orderbook/level2', 'market/orderbook/level2_20', 'market/orderbook/level2_100', 'market/orderbook/level3', 'market/histories', 'market/candles', 'market/stats', 'currencies', 'currencies/{currency}', 'prices', 'mark-price/{symbol}/current', 'margin/config', ], 'post': [ 'bullet-public', ], }, 'private': { 'get': [ 'accounts', 'accounts/{accountId}', 'accounts/{accountId}/ledgers', 'accounts/{accountId}/holds', 'accounts/transferable', 'sub/user', 'sub-accounts', 'sub-accounts/{subUserId}', 'deposit-addresses', 'deposits', 'hist-deposits', 'hist-orders', 'hist-withdrawals', 'withdrawals', 'withdrawals/quotas', 'orders', 'orders/{orderId}', 'limit/orders', 'fills', 'limit/fills', 'margin/account', 'margin/borrow', 'margin/borrow/outstanding', 'margin/borrow/borrow/repaid', 'margin/lend/active', 'margin/lend/done', 'margin/lend/trade/unsettled', 'margin/lend/trade/settled', 'margin/lend/assets', 'margin/market', 'margin/margin/trade/last', ], 'post': [ 'accounts', 'accounts/inner-transfer', 'accounts/sub-transfer', 'deposit-addresses', 'withdrawals', 'orders', 'orders/multi', 'margin/borrow', 'margin/repay/all', 'margin/repay/single', 'margin/lend', 'margin/toggle-auto-lend', 'bullet-private', ], 'delete': [ 'withdrawals/{withdrawalId}', 'orders', 'orders/{orderId}', 'margin/lend/{orderId}', ], }, }, 'timeframes': { '1m': '1min', '3m': '3min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '2h': '2hour', '4h': '4hour', '6h': '6hour', '8h': '8hour', '12h': '12hour', '1d': '1day', '1w': '1week', }, 'exceptions': { 'exact': { 'order not exist': OrderNotFound, 'order not exist.': OrderNotFound, // duplicated error temporarily 'order_not_exist': OrderNotFound, // {"code":"order_not_exist","msg":"order_not_exist"} ¯\_(ツ)_/¯ 'order_not_exist_or_not_allow_to_cancel': InvalidOrder, // {"code":"400100","msg":"order_not_exist_or_not_allow_to_cancel"} 'Order size below the minimum requirement.': InvalidOrder, // {"code":"400100","msg":"Order size below the minimum requirement."} 'The withdrawal amount is below the minimum requirement.': ExchangeError, // {"code":"400100","msg":"The withdrawal amount is below the minimum requirement."} '400': BadRequest, '401': AuthenticationError, '403': NotSupported, '404': NotSupported, '405': NotSupported, '429': RateLimitExceeded, '500': ExchangeError, '503': ExchangeNotAvailable, '200004': InsufficientFunds, '230003': InsufficientFunds, // {"code":"230003","msg":"Balance insufficient!"} '260100': InsufficientFunds, // {"code":"260100","msg":"account.noBalance"} '300000': InvalidOrder, '400000': BadSymbol, '400001': AuthenticationError, '400002': InvalidNonce, '400003': AuthenticationError, '400004': AuthenticationError, '400005': AuthenticationError, '400006': AuthenticationError, '400007': AuthenticationError, '400008': NotSupported, '400100': BadRequest, '411100': AccountSuspended, '415000': BadRequest, // {"code":"415000","msg":"Unsupported Media Type"} '500000': ExchangeError, }, 'broad': { 'Exceeded the access frequency': RateLimitExceeded, }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.001, 'maker': 0.001, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, 'commonCurrencies': { 'HOT': 'HOTNOW', 'EDGE': 'DADI', // https://github.com/ccxt/ccxt/issues/5756 'WAX': 'WAXP', 'TRY': 'Trias', }, 'options': { 'version': 'v1', 'symbolSeparator': '-', 'fetchMyTradesMethod': 'private_get_fills', 'fetchBalance': { 'type': 'trade', // or 'main' }, // endpoint versions 'versions': { 'public': { 'GET': { 'market/orderbook/level{level}': 'v1', 'market/orderbook/level2': 'v2', 'market/orderbook/level2_20': 'v1', 'market/orderbook/level2_100': 'v1', }, }, 'private': { 'POST': { 'accounts/inner-transfer': 'v2', 'accounts/sub-transfer': 'v2', }, }, }, }, }); } nonce () { return this.milliseconds (); } async loadTimeDifference () { const response = await this.publicGetTimestamp (); const after = this.milliseconds (); const kucoinTime = this.safeInteger (response, 'data'); this.options['timeDifference'] = parseInt (after - kucoinTime); return this.options['timeDifference']; } async fetchTime (params = {}) { const response = await this.publicGetTimestamp (params); // // { // "code":"200000", // "msg":"success", // "data":1546837113087 // } // return this.safeInteger (response, 'data'); } async fetchMarkets (params = {}) { const response = await this.publicGetSymbols (params); // // { // quoteCurrency: 'BTC', // symbol: 'KCS-BTC', // quoteMaxSize: '9999999', // quoteIncrement: '0.000001', // baseMinSize: '0.01', // quoteMinSize: '0.00001', // enableTrading: true, // priceIncrement: '0.00000001', // name: 'KCS-BTC', // baseIncrement: '0.01', // baseMaxSize: '9999999', // baseCurrency: 'KCS' // } // const data = response['data']; const result = []; for (let i = 0; i < data.length; i++) { const market = data[i]; const id = this.safeString (market, 'symbol'); const [ baseId, quoteId ] = id.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const active = this.safeValue (market, 'enableTrading'); const baseMaxSize = this.safeFloat (market, 'baseMaxSize'); const baseMinSize = this.safeFloat (market, 'baseMinSize'); const quoteMaxSize = this.safeFloat (market, 'quoteMaxSize'); const quoteMinSize = this.safeFloat (market, 'quoteMinSize'); // const quoteIncrement = this.safeFloat (market, 'quoteIncrement'); const precision = { 'amount': this.precisionFromString (this.safeString (market, 'baseIncrement')), 'price': this.precisionFromString (this.safeString (market, 'priceIncrement')), }; const limits = { 'amount': { 'min': baseMinSize, 'max': baseMaxSize, }, 'price': { 'min': this.safeFloat (market, 'priceIncrement'), 'max': quoteMaxSize / baseMinSize, }, 'cost': { 'min': quoteMinSize, 'max': quoteMaxSize, }, }; result.push ({ 'id': id, 'symbol': symbol, 'baseId': baseId, 'quoteId': quoteId, 'base': base, 'quote': quote, 'active': active, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencies (params); // // { // precision: 10, // name: 'KCS', // fullName: 'KCS shares', // currency: 'KCS' // } // const responseData = response['data']; const result = {}; for (let i = 0; i < responseData.length; i++) { const entry = responseData[i]; const id = this.safeString (entry, 'currency'); const name = this.safeString (entry, 'fullName'); const code = this.safeCurrencyCode (id); const precision = this.safeInteger (entry, 'precision'); result[code] = { 'id': id, 'name': name, 'code': code, 'precision': precision, 'info': entry, 'active': undefined, 'fee': undefined, 'limits': this.limits, }; } return result; } async fetchAccounts (params = {}) { const response = await this.privateGetAccounts (params); // // { // code: "200000", // data: [ // { // balance: "0.00009788", // available: "0.00009788", // holds: "0", // currency: "BTC", // id: "5c6a4fd399a1d81c4f9cc4d0", // type: "trade" // }, // { // balance: "0.00000001", // available: "0.00000001", // holds: "0", // currency: "ETH", // id: "5c6a49ec99a1d819392e8e9f", // type: "trade" // } // ] // } // const data = this.safeValue (response, 'data'); const result = []; for (let i = 0; i < data.length; i++) { const account = data[i]; const accountId = this.safeString (account, 'id'); const currencyId = this.safeString (account, 'currency'); const code = this.safeCurrencyCode (currencyId); const type = this.safeString (account, 'type'); // main or trade result.push ({ 'id': accountId, 'type': type, 'currency': code, 'info': account, }); } return result; } async fetchFundingFee (code, params = {}) { const currencyId = this.currencyId (code); const request = { 'currency': currencyId, }; const response = await this.privateGetWithdrawalsQuotas (this.extend (request, params)); const data = response['data']; const withdrawFees = {}; withdrawFees[code] = this.safeFloat (data, 'withdrawMinFee'); return { 'info': response, 'withdraw': withdrawFees, 'deposit': {}, }; } parseTicker (ticker, market = undefined) { // // { // symbol: "ETH-BTC", // high: "0.019518", // vol: "7997.82836194", // last: "0.019329", // low: "0.019", // buy: "0.019329", // sell: "0.01933", // changePrice: "-0.000139", // time: 1580553706304, // averagePrice: "0.01926386", // changeRate: "-0.0071", // volValue: "154.40791568183474" // } // // { // "trading": true, // "symbol": "KCS-BTC", // "buy": 0.00011, // "sell": 0.00012, // "sort": 100, // "volValue": 3.13851792584, //total // "baseCurrency": "KCS", // "market": "BTC", // "quoteCurrency": "BTC", // "symbolCode": "KCS-BTC", // "datetime": 1548388122031, // "high": 0.00013, // "vol": 27514.34842, // "low": 0.0001, // "changePrice": -1.0e-5, // "changeRate": -0.0769, // "lastTradedPrice": 0.00012, // "board": 0, // "mark": 0 // } // let percentage = this.safeFloat (ticker, 'changeRate'); if (percentage !== undefined) { percentage = percentage * 100; } const last = this.safeFloat2 (ticker, 'last', 'lastTradedPrice'); let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const timestamp = this.safeInteger2 (ticker, 'time', 'datetime'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': this.safeFloat (ticker, 'changePrice'), 'percentage': percentage, 'average': this.safeFloat (ticker, 'averagePrice'), 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, 'volValue'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarketAllTickers (params); // // { // "code": "200000", // "data": { // "date": 1550661940645, // "ticker": [ // 'buy': '0.00001168', // 'changePrice': '-0.00000018', // 'changeRate': '-0.0151', // 'datetime': 1550661146316, // 'high': '0.0000123', // 'last': '0.00001169', // 'low': '0.00001159', // 'sell': '0.00001182', // 'symbol': 'LOOM-BTC', // 'vol': '44399.5669' // }, // ] // } // const data = this.safeValue (response, 'data', {}); const tickers = this.safeValue (data, 'ticker', []); const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = this.parseTicker (tickers[i]); const symbol = this.safeString (ticker, 'symbol'); if (symbol !== undefined) { result[symbol] = ticker; } } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetMarketStats (this.extend (request, params)); // // { // "code": "200000", // "data": { // 'buy': '0.00001168', // 'changePrice': '-0.00000018', // 'changeRate': '-0.0151', // 'datetime': 1550661146316, // 'high': '0.0000123', // 'last': '0.00001169', // 'low': '0.00001159', // 'sell': '0.00001182', // 'symbol': 'LOOM-BTC', // 'vol': '44399.5669' // }, // } // return this.parseTicker (response['data'], market); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // [ // "1545904980", // Start time of the candle cycle // "0.058", // opening price // "0.049", // closing price // "0.058", // highest price // "0.049", // lowest price // "0.018", // base volume // "0.000945", // quote volume // ] // return [ parseInt (ohlcv[0]) * 1000, parseFloat (ohlcv[1]), parseFloat (ohlcv[3]), parseFloat (ohlcv[4]), parseFloat (ohlcv[2]), parseFloat (ohlcv[5]), ]; } async fetchOHLCV (symbol, timeframe = '15m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const marketId = market['id']; const request = { 'symbol': marketId, 'type': this.timeframes[timeframe], }; const duration = this.parseTimeframe (timeframe) * 1000; let endAt = this.milliseconds (); // required param if (since !== undefined) { request['startAt'] = parseInt (Math.floor (since / 1000)); if (limit === undefined) { // https://docs.kucoin.com/#get-klines // https://docs.kucoin.com/#details // For each query, the system would return at most 1500 pieces of data. // To obtain more data, please page the data by time. limit = this.safeInteger (this.options, 'fetchOHLCVLimit', 1500); } endAt = this.sum (since, limit * duration); } else if (limit !== undefined) { since = endAt - limit * duration; request['startAt'] = parseInt (Math.floor (since / 1000)); } request['endAt'] = parseInt (Math.floor (endAt / 1000)); const response = await this.publicGetMarketCandles (this.extend (request, params)); const responseData = this.safeValue (response, 'data', []); return this.parseOHLCVs (responseData, market, timeframe, since, limit); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currencyId = this.currencyId (code); const request = { 'currency': currencyId }; const response = await this.privatePostDepositAddresses (this.extend (request, params)); // BCH {"code":"200000","data":{"address":"bitcoincash:qza3m4nj9rx7l9r0cdadfqxts6f92shvhvr5ls4q7z","memo":""}} // BTC {"code":"200000","data":{"address":"36SjucKqQpQSvsak9A7h6qzFjrVXpRNZhE","memo":""}} const data = this.safeValue (response, 'data', {}); let address = this.safeString (data, 'address'); // BCH/BSV is returned with a "bitcoincash:" prefix, which we cut off here and only keep the address if (address !== undefined) { address = address.replace ('bitcoincash:', ''); } const tag = this.safeString (data, 'memo'); this.checkAddress (address); return { 'info': response, 'currency': code, 'address': address, 'tag': tag, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currencyId = this.currencyId (code); const request = { 'currency': currencyId }; const response = await this.privateGetDepositAddresses (this.extend (request, params)); // BCH {"code":"200000","data":{"address":"bitcoincash:qza3m4nj9rx7l9r0cdadfqxts6f92shvhvr5ls4q7z","memo":""}} // BTC {"code":"200000","data":{"address":"36SjucKqQpQSvsak9A7h6qzFjrVXpRNZhE","memo":""}} const data = this.safeValue (response, 'data', {}); let address = this.safeString (data, 'address'); // BCH/BSV is returned with a "bitcoincash:" prefix, which we cut off here and only keep the address if (address !== undefined) { address = address.replace ('bitcoincash:', ''); } const tag = this.safeString (data, 'memo'); this.checkAddress (address); return { 'info': response, 'currency': code, 'address': address, 'tag': tag, }; } async fetchL3OrderBook (symbol, limit = undefined, params = {}) { return await this.fetchOrderBook (symbol, limit, { 'level': 3 }); } async fetchOrderBook (symbol, limit = undefined, params = {}) { const level = this.safeInteger (params, 'level', 2); let levelLimit = level.toString (); if (level === '2') { if (limit !== undefined) { if ((limit !== 20) && (limit !== 100)) { throw new ExchangeError (this.id + ' fetchOrderBook limit argument must be undefined, 20 or 100'); } levelLimit += '_' + limit.toString (); } } await this.loadMarkets (); const marketId = this.marketId (symbol); const request = { 'symbol': marketId, 'level': levelLimit }; const response = await this.publicGetMarketOrderbookLevelLevel (this.extend (request, params)); // // 'market/orderbook/level2' // 'market/orderbook/level2_20' // 'market/orderbook/level2_100' // // { // "code":"200000", // "data":{ // "sequence":"1583235112106", // "asks":[ // // ... // ["0.023197","12.5067468"], // ["0.023194","1.8"], // ["0.023191","8.1069672"] // ], // "bids":[ // ["0.02319","1.6000002"], // ["0.023189","2.2842325"], // ], // "time":1586584067274 // } // } // // 'market/orderbook/level3' // // { // "code":"200000", // "data":{ // "sequence":"1583731857120", // "asks":[ // // id, price, size, timestamp in nanoseconds // ["5e915f8acd26670009675300","6925.7","0.2","1586585482194286069"], // ["5e915f8ace35a200090bba48","6925.7","0.001","1586585482229569826"], // ["5e915f8a8857740009ca7d33","6926","0.00001819","1586585482149148621"], // ], // "bids":[ // ["5e915f8acca406000ac88194","6925.6","0.05","1586585482384384842"], // ["5e915f93cd26670009676075","6925.6","0.08","1586585491334914600"], // ["5e915f906aa6e200099b49f6","6925.4","0.2","1586585488941126340"], // ], // "time":1586585492487 // } // } // const data = this.safeValue (response, 'data', {}); const timestamp = this.safeInteger (data, 'time'); const orderbook = this.parseOrderBook (data, timestamp, 'bids', 'asks', level - 2, level - 1); orderbook['nonce'] = this.safeInteger (data, 'sequence'); return orderbook; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const marketId = this.marketId (symbol); // required param, cannot be used twice const clientOid = this.uuid (); const request = { 'clientOid': clientOid, 'side': side, 'symbol': marketId, 'type': type, }; if (type !== 'market') { request['price'] = this.priceToPrecision (symbol, price); request['size'] = this.amountToPrecision (symbol, amount); } else { if (this.safeValue (params, 'quoteAmount')) { // used to create market order by quote amount - https://github.com/ccxt/ccxt/issues/4876 request['funds'] = this.amountToPrecision (symbol, amount); } else { request['size'] = this.amountToPrecision (symbol, amount); } } const response = await this.privatePostOrders (this.extend (request, params)); // // { // code: '200000', // data: { // "orderId": "5bd6e9286d99522a52e458de" // } // } // const data = this.safeValue (response, 'data', {}); const timestamp = this.milliseconds (); const order = { 'id': this.safeString (data, 'orderId'), 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': undefined, 'filled': undefined, 'remaining': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': undefined, 'status': 'open', 'clientOrderId': clientOid, 'info': data, }; if (!this.safeValue (params, 'quoteAmount')) { order['amount'] = amount; } return order; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'orderId': id }; const response = await this.privateDeleteOrdersOrderId (this.extend (request, params)); return response; } async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'status': status, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['startAt'] = since; } if (limit !== undefined) { request['pageSize'] = limit; } const response = await this.privateGetOrders (this.extend (request, params)); // // { // code: '200000', // data: { // "currentPage": 1, // "pageSize": 1, // "totalNum": 153408, // "totalPage": 153408, // "items": [ // { // "id": "5c35c02703aa673ceec2a168", //orderid // "symbol": "BTC-USDT", //symbol // "opType": "DEAL", // operation type,deal is pending order,cancel is cancel order // "type": "limit", // order type,e.g. limit,markrt,stop_limit. // "side": "buy", // transaction direction,include buy and sell // "price": "10", // order price // "size": "2", // order quantity // "funds": "0", // order funds // "dealFunds": "0.166", // deal funds // "dealSize": "2", // deal quantity // "fee": "0", // fee // "feeCurrency": "USDT", // charge fee currency // "stp": "", // self trade prevention,include CN,CO,DC,CB // "stop": "", // stop type // "stopTriggered": false, // stop order is triggered // "stopPrice": "0", // stop price // "timeInForce": "GTC", // time InForce,include GTC,GTT,IOC,FOK // "postOnly": false, // postOnly // "hidden": false, // hidden order // "iceberg": false, // iceberg order // "visibleSize": "0", // display quantity for iceberg order // "cancelAfter": 0, // cancel orders time,requires timeInForce to be GTT // "channel": "IOS", // order source // "clientOid": "", // user-entered order unique mark // "remark": "", // remark // "tags": "", // tag order source // "isActive": false, // status before unfilled or uncancelled // "cancelExist": false, // order cancellation transaction record // "createdAt": 1547026471000 // time // }, // ] // } // } const responseData = this.safeValue (response, 'data', {}); const orders = this.safeValue (responseData, 'items', []); return this.parseOrders (orders, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus ('done', symbol, since, limit, params); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus ('active', symbol, since, limit, params); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const response = await this.privateGetOrdersOrderId (this.extend (request, params)); const responseData = response['data']; return this.parseOrder (responseData, market); } parseOrder (order, market = undefined) { // // fetchOpenOrders, fetchClosedOrders // // { // "id": "5c35c02703aa673ceec2a168", //orderid // "symbol": "BTC-USDT", //symbol // "opType": "DEAL", // operation type,deal is pending order,cancel is cancel order // "type": "limit", // order type,e.g. limit,markrt,stop_limit. // "side": "buy", // transaction direction,include buy and sell // "price": "10", // order price // "size": "2", // order quantity // "funds": "0", // order funds // "dealFunds": "0.166", // deal funds // "dealSize": "2", // deal quantity // "fee": "0", // fee // "feeCurrency": "USDT", // charge fee currency // "stp": "", // self trade prevention,include CN,CO,DC,CB // "stop": "", // stop type // "stopTriggered": false, // stop order is triggered // "stopPrice": "0", // stop price // "timeInForce": "GTC", // time InForce,include GTC,GTT,IOC,FOK // "postOnly": false, // postOnly // "hidden": false, // hidden order // "iceberg": false, // iceberg order // "visibleSize": "0", // display quantity for iceberg order // "cancelAfter": 0, // cancel orders time,requires timeInForce to be GTT // "channel": "IOS", // order source // "clientOid": "", // user-entered order unique mark // "remark": "", // remark // "tags": "", // tag order source // "isActive": false, // status before unfilled or uncancelled // "cancelExist": false, // order cancellation transaction record // "createdAt": 1547026471000 // time // } // let symbol = undefined; const marketId = this.safeString (order, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } market = this.safeValue (this.markets_by_id, marketId); } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const orderId = this.safeString (order, 'id'); const type = this.safeString (order, 'type'); const timestamp = this.safeInteger (order, 'createdAt'); const datetime = this.iso8601 (timestamp); let price = this.safeFloat (order, 'price'); const side = this.safeString (order, 'side'); const feeCurrencyId = this.safeString (order, 'feeCurrency'); const feeCurrency = this.safeCurrencyCode (feeCurrencyId); const feeCost = this.safeFloat (order, 'fee'); const amount = this.safeFloat (order, 'size'); const filled = this.safeFloat (order, 'dealSize'); const cost = this.safeFloat (order, 'dealFunds'); const remaining = amount - filled; // bool let status = order['isActive'] ? 'open' : 'closed'; status = order['cancelExist'] ? 'canceled' : status; const fee = { 'currency': feeCurrency, 'cost': feeCost, }; if (type === 'market') { if (price === 0.0) { if ((cost !== undefined) && (filled !== undefined)) { if ((cost > 0) && (filled > 0)) { price = cost / filled; } } } } const clientOrderId = this.safeString (order, 'clientOid'); return { 'id': orderId, 'clientOrderId': clientOrderId, 'symbol': symbol, 'type': type, 'side': side, 'amount': amount, 'price': price, 'cost': cost, 'filled': filled, 'remaining': remaining, 'timestamp': timestamp, 'datetime': datetime, 'fee': fee, 'status': status, 'info': order, 'lastTradeTimestamp': undefined, 'average': undefined, 'trades': undefined, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['pageSize'] = limit; } const method = this.options['fetchMyTradesMethod']; let parseResponseData = false; if (method === 'private_get_fills') { // does not return trades earlier than 2019-02-18T00:00:00Z if (since !== undefined) { // only returns trades up to one week after the since param request['startAt'] = since; } } else if (method === 'private_get_limit_fills') { // does not return trades earlier than 2019-02-18T00:00:00Z // takes no params // only returns first 1000 trades (not only "in the last 24 hours" as stated in the docs) parseResponseData = true; } else if (method === 'private_get_hist_orders') { // despite that this endpoint is called `HistOrders` // it returns historical trades instead of orders // returns trades earlier than 2019-02-18T00:00:00Z only if (since !== undefined) { request['startAt'] = parseInt (since / 1000); } } else { throw new ExchangeError (this.id + ' invalid fetchClosedOrder method'); } const response = await this[method] (this.extend (request, params)); // // { // "currentPage": 1, // "pageSize": 50, // "totalNum": 1, // "totalPage": 1, // "items": [ // { // "symbol":"BTC-USDT", // symbol // "tradeId":"5c35c02709e4f67d5266954e", // trade id // "orderId":"5c35c02703aa673ceec2a168", // order id // "counterOrderId":"5c1ab46003aa676e487fa8e3", // counter order id // "side":"buy", // transaction direction,include buy and sell // "liquidity":"taker", // include taker and maker // "forceTaker":true, // forced to become taker // "price":"0.083", // order price // "size":"0.8424304", // order quantity // "funds":"0.0699217232", // order funds // "fee":"0", // fee // "feeRate":"0", // fee rate // "feeCurrency":"USDT", // charge fee currency // "stop":"", // stop type // "type":"limit", // order type, e.g. limit, market, stop_limit. // "createdAt":1547026472000 // time // }, // //------------------------------------------------------ // // v1 (historical) trade response structure // { // "symbol": "SNOV-ETH", // "dealPrice": "0.0000246", // "dealValue": "0.018942", // "amount": "770", // "fee": "0.00001137", // "side": "sell", // "createdAt": 1540080199 // "id":"5c4d389e4c8c60413f78e2e5", // } // ] // } // const data = this.safeValue (response, 'data', {}); let trades = undefined; if (parseResponseData) { trades = data; } else { trades = this.safeValue (data, 'items', []); } return this.parseTrades (trades, market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (since !== undefined) { request['startAt'] = Math.floor (since / 1000); } if (limit !== undefined) { request['pageSize'] = limit; } const response = await this.publicGetMarketHistories (this.extend (request, params)); // // { // "code": "200000", // "data": [ // { // "sequence": "1548764654235", // "side": "sell", // "size":"0.6841354", // "price":"0.03202", // "time":1548848575203567174 // } // ] // } // const trades = this.safeValue (response, 'data', []); return this.parseTrades (trades, market, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "sequence": "1548764654235", // "side": "sell", // "size":"0.6841354", // "price":"0.03202", // "time":1548848575203567174 // } // // { // sequence: '1568787654360', // symbol: 'BTC-USDT', // side: 'buy', // size: '0.00536577', // price: '9345', // takerOrderId: '5e356c4a9f1a790008f8d921', // time: '1580559434436443257', // type: 'match', // makerOrderId: '5e356bffedf0010008fa5d7f', // tradeId: '5e356c4aeefabd62c62a1ece' // } // // fetchMyTrades (private) v2 // // { // "symbol":"BTC-USDT", // "tradeId":"5c35c02709e4f67d5266954e", // "orderId":"5c35c02703aa673ceec2a168", // "counterOrderId":"5c1ab46003aa676e487fa8e3", // "side":"buy", // "liquidity":"taker", // "forceTaker":true, // "price":"0.083", // "size":"0.8424304", // "funds":"0.0699217232", // "fee":"0", // "feeRate":"0", // "feeCurrency":"USDT", // "stop":"", // "type":"limit", // "createdAt":1547026472000 // } // // fetchMyTrades v2 alternative format since 2019-05-21 https://github.com/ccxt/ccxt/pull/5162 // // { // symbol: "OPEN-BTC", // forceTaker: false, // orderId: "5ce36420054b4663b1fff2c9", // fee: "0", // feeCurrency: "", // type: "", // feeRate: "0", // createdAt: 1558417615000, // size: "12.8206", // stop: "", // price: "0", // funds: "0", // tradeId: "5ce390cf6e0db23b861c6e80" // } // // fetchMyTrades (private) v1 (historical) // // { // "symbol": "SNOV-ETH", // "dealPrice": "0.0000246", // "dealValue": "0.018942", // "amount": "770", // "fee": "0.00001137", // "side": "sell", // "createdAt": 1540080199 // "id":"5c4d389e4c8c60413f78e2e5", // } // let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } const id = this.safeString2 (trade, 'tradeId', 'id'); const orderId = this.safeString (trade, 'orderId'); const takerOrMaker = this.safeString (trade, 'liquidity'); const amount = this.safeFloat2 (trade, 'size', 'amount'); let timestamp = this.safeInteger (trade, 'time'); if (timestamp !== undefined) { timestamp = parseInt (timestamp / 1000000); } else { timestamp = this.safeInteger (trade, 'createdAt'); // if it's a historical v1 trade, the exchange returns timestamp in seconds if (('dealValue' in trade) && (timestamp !== undefined)) { timestamp = timestamp * 1000; } } const price = this.safeFloat2 (trade, 'price', 'dealPrice'); const side = this.safeString (trade, 'side'); let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'feeCurrency'); let feeCurrency = this.safeCurrencyCode (feeCurrencyId); if (feeCurrency === undefined) { if (market !== undefined) { feeCurrency = (side === 'sell') ? market['quote'] : market['base']; } } fee = { 'cost': feeCost, 'currency': feeCurrency, 'rate': this.safeFloat (trade, 'feeRate'), }; } const type = this.safeString (trade, 'type'); let cost = this.safeFloat2 (trade, 'funds', 'dealValue'); if (cost === undefined) { if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } } return { 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { await this.loadMarkets (); this.checkAddress (address); const currency = this.currencyId (code); const request = { 'currency': currency, 'address': address, 'amount': amount, }; if (tag !== undefined) { request['memo'] = tag; } const response = await this.privatePostWithdrawals (this.extend (request, params)); // // https://github.com/ccxt/ccxt/issues/5558 // // { // "code": 200000, // "data": { // "withdrawalId": "abcdefghijklmnopqrstuvwxyz" // } // } // const data = this.safeValue (response, 'data', {}); return { 'id': this.safeString (data, 'withdrawalId'), 'info': response, }; } parseTransactionStatus (status) { const statuses = { 'SUCCESS': 'ok', 'PROCESSING': 'ok', 'FAILURE': 'failed', }; return this.safeString (statuses, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989", // "memo": "5c247c8a03aa677cea2a251d", // "amount": 1, // "fee": 0.0001, // "currency": "KCS", // "isInner": false, // "walletTxId": "5bbb57386d99522d9f954c5a@test004", // "status": "SUCCESS", // "createdAt": 1544178843000, // "updatedAt": 1544178891000 // } // // fetchWithdrawals // // { // "id": "5c2dc64e03aa675aa263f1ac", // "address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9", // "memo": "", // "currency": "ETH", // "amount": 1.0000000, // "fee": 0.0100000, // "walletTxId": "3e2414d82acce78d38be7fe9", // "isInner": false, // "status": "FAILURE", // "createdAt": 1546503758000, // "updatedAt": 1546504603000 // } // const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); let address = this.safeString (transaction, 'address'); const amount = this.safeFloat (transaction, 'amount'); let txid = this.safeString (transaction, 'walletTxId'); if (txid !== undefined) { const txidParts = txid.split ('@'); const numTxidParts = txidParts.length; if (numTxidParts > 1) { if (address === undefined) { if (txidParts[1].length > 1) { address = txidParts[1]; } } } txid = txidParts[0]; } let type = (txid === undefined) ? 'withdrawal' : 'deposit'; const rawStatus = this.safeString (transaction, 'status'); const status = this.parseTransactionStatus (rawStatus); let fee = undefined; const feeCost = this.safeFloat (transaction, 'fee'); if (feeCost !== undefined) { let rate = undefined; if (amount !== undefined) { rate = feeCost / amount; } fee = { 'cost': feeCost, 'rate': rate, 'currency': code, }; } const tag = this.safeString (transaction, 'memo'); let timestamp = this.safeInteger2 (transaction, 'createdAt', 'createAt'); const id = this.safeString (transaction, 'id'); let updated = this.safeInteger (transaction, 'updatedAt'); const isV1 = !('createdAt' in transaction); // if it's a v1 structure if (isV1) { type = ('address' in transaction) ? 'withdrawal' : 'deposit'; if (timestamp !== undefined) { timestamp = timestamp * 1000; } if (updated !== undefined) { updated = updated * 1000; } } return { 'id': id, 'address': address, 'tag': tag, 'currency': code, 'amount': amount, 'txid': txid, 'type': type, 'status': status, 'fee': fee, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'updated': updated, 'info': transaction, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (limit !== undefined) { request['pageSize'] = limit; } let method = 'privateGetDeposits'; if (since !== undefined) { // if since is earlier than 2019-02-18T00:00:00Z if (since < 1550448000000) { request['startAt'] = parseInt (since / 1000); method = 'privateGetHistDeposits'; } else { request['startAt'] = since; } } const response = await this[method] (this.extend (request, params)); // // { // code: '200000', // data: { // "currentPage": 1, // "pageSize": 5, // "totalNum": 2, // "totalPage": 1, // "items": [ // //-------------------------------------------------- // // version 2 deposit response structure // { // "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989", // "memo": "5c247c8a03aa677cea2a251d", // "amount": 1, // "fee": 0.0001, // "currency": "KCS", // "isInner": false, // "walletTxId": "5bbb57386d99522d9f954c5a@test004", // "status": "SUCCESS", // "createdAt": 1544178843000, // "updatedAt": 1544178891000 // }, // //-------------------------------------------------- // // version 1 (historical) deposit response structure // { // "currency": "BTC", // "createAt": 1528536998, // "amount": "0.03266638", // "walletTxId": "55c643bc2c68d6f17266383ac1be9e454038864b929ae7cee0bc408cc5c869e8@12ffGWmMMD1zA1WbFm7Ho3JZ1w6NYXjpFk@234", // "isInner": false, // "status": "SUCCESS", // } // ] // } // } // const responseData = response['data']['items']; return this.parseTransactions (responseData, currency, since, limit, { 'type': 'deposit' }); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (limit !== undefined) { request['pageSize'] = limit; } let method = 'privateGetWithdrawals'; if (since !== undefined) { // if since is earlier than 2019-02-18T00:00:00Z if (since < 1550448000000) { request['startAt'] = parseInt (since / 1000); method = 'privateGetHistWithdrawals'; } else { request['startAt'] = since; } } const response = await this[method] (this.extend (request, params)); // // { // code: '200000', // data: { // "currentPage": 1, // "pageSize": 5, // "totalNum": 2, // "totalPage": 1, // "items": [ // //-------------------------------------------------- // // version 2 withdrawal response structure // { // "id": "5c2dc64e03aa675aa263f1ac", // "address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9", // "memo": "", // "currency": "ETH", // "amount": 1.0000000, // "fee": 0.0100000, // "walletTxId": "3e2414d82acce78d38be7fe9", // "isInner": false, // "status": "FAILURE", // "createdAt": 1546503758000, // "updatedAt": 1546504603000 // }, // //-------------------------------------------------- // // version 1 (historical) withdrawal response structure // { // "currency": "BTC", // "createAt": 1526723468, // "amount": "0.534", // "address": "33xW37ZSW4tQvg443Pc7NLCAs167Yc2XUV", // "walletTxId": "aeacea864c020acf58e51606169240e96774838dcd4f7ce48acf38e3651323f4", // "isInner": false, // "status": "SUCCESS" // } // ] // } // } // const responseData = response['data']['items']; return this.parseTransactions (responseData, currency, since, limit, { 'type': 'withdrawal' }); } async fetchBalance (params = {}) { await this.loadMarkets (); let type = undefined; const request = {}; if ('type' in params) { type = params['type']; if (type !== undefined) { request['type'] = type; } params = this.omit (params, 'type'); } else { const options = this.safeValue (this.options, 'fetchBalance', {}); type = this.safeString (options, 'type', 'trade'); } const response = await this.privateGetAccounts (this.extend (request, params)); // // { // "code":"200000", // "data":[ // {"balance":"0.00009788","available":"0.00009788","holds":"0","currency":"BTC","id":"5c6a4fd399a1d81c4f9cc4d0","type":"trade"}, // {"balance":"3.41060034","available":"3.41060034","holds":"0","currency":"SOUL","id":"5c6a4d5d99a1d8182d37046d","type":"trade"}, // {"balance":"0.01562641","available":"0.01562641","holds":"0","currency":"NEO","id":"5c6a4f1199a1d8165a99edb1","type":"trade"}, // ] // } // const data = this.safeValue (response, 'data', []); const result = { 'info': response }; for (let i = 0; i < data.length; i++) { const balance = data[i]; const balanceType = this.safeString (balance, 'type'); if (balanceType === type) { const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balance, 'balance'); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'holds'); result[code] = account; } } return this.parseBalance (result); } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { if (code === undefined) { throw new ArgumentsRequired (this.id + ' fetchLedger requires a code param'); } await this.loadMarkets (); await this.loadAccounts (); const currency = this.currency (code); let accountId = this.safeString (params, 'accountId'); if (accountId === undefined) { for (let i = 0; i < this.accounts.length; i++) { const account = this.accounts[i]; if (account['currency'] === code && account['type'] === 'main') { accountId = account['id']; break; } } } if (accountId === undefined) { throw new ExchangeError (this.id + ' ' + code + 'main account is not loaded in loadAccounts'); } const request = { 'accountId': accountId, }; if (since !== undefined) { request['startAt'] = Math.floor (since / 1000); } const response = await this.privateGetAccountsAccountIdLedgers (this.extend (request, params)); // // { // code: '200000', // data: { // totalNum: 1, // totalPage: 1, // pageSize: 50, // currentPage: 1, // items: [ // { // createdAt: 1561897880000, // amount: '0.0111123', // bizType: 'Exchange', // balance: '0.13224427', // fee: '0.0000111', // context: '{"symbol":"KCS-ETH","orderId":"5d18ab98c788c6426188296f","tradeId":"5d18ab9818996813f539a806"}', // currency: 'ETH', // direction: 'out' // } // ] // } // } // const items = response['data']['items']; return this.parseLedger (items, currency, since, limit); } parseLedgerEntry (item, currency = undefined) { // // trade // // { // createdAt: 1561897880000, // amount: '0.0111123', // bizType: 'Exchange', // balance: '0.13224427', // fee: '0.0000111', // context: '{"symbol":"KCS-ETH","orderId":"5d18ab98c788c6426188296f","tradeId":"5d18ab9818996813f539a806"}', // currency: 'ETH', // direction: 'out' // } // // withdrawal // // { // createdAt: 1561900264000, // amount: '0.14333217', // bizType: 'Withdrawal', // balance: '0', // fee: '0.01', // context: '{"orderId":"5d18b4e687111437cf1c48b9","txId":"0x1d136ee065c5c4c5caa293faa90d43e213c953d7cdd575c89ed0b54eb87228b8"}', // currency: 'ETH', // direction: 'out' // } // const currencyId = this.safeString (item, 'currency'); const code = this.safeCurrencyCode (currencyId, currency); const fee = { 'cost': this.safeFloat (item, 'fee'), 'code': code, }; const amount = this.safeFloat (item, 'amount'); const after = this.safeFloat (item, 'balance'); const direction = this.safeString (item, 'direction'); let before = undefined; if (after !== undefined && amount !== undefined) { const difference = (direction === 'out') ? amount : -amount; before = this.sum (after, difference); } const timestamp = this.safeInteger (item, 'createdAt'); const type = this.parseLedgerEntryType (this.safeString (item, 'bizType')); const contextString = this.safeString (item, 'context'); let id = undefined; let referenceId = undefined; if (this.isJsonEncodedObject (contextString)) { const context = this.parseJson (contextString); id = this.safeString (context, 'orderId'); if (type === 'trade') { referenceId = this.safeString (context, 'tradeId'); } else if (type === 'transaction') { referenceId = this.safeString (context, 'txId'); } } return { 'id': id, 'currency': code, 'account': undefined, 'referenceAccount': undefined, 'referenceId': referenceId, 'status': undefined, 'amount': amount, 'before': before, 'after': after, 'fee': fee, 'direction': direction, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'type': type, 'info': item, }; } parseLedgerEntryType (type) { const types = { 'Exchange': 'trade', 'Withdrawal': 'transaction', 'Deposit': 'transaction', 'Transfer': 'transfer', }; return this.safeString (types, type, type); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { // // the v2 URL is https://openapi-v2.kucoin.com/api/v1/endpoint // † ↑ // const versions = this.safeValue (this.options, 'versions', {}); const apiVersions = this.safeValue (versions, api); const methodVersions = this.safeValue (apiVersions, method, {}); const defaultVersion = this.safeString (methodVersions, path, this.options['version']); const version = this.safeString (params, 'version', defaultVersion); params = this.omit (params, 'version'); let endpoint = '/api/' + version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); let endpart = ''; headers = (headers !== undefined) ? headers : {}; if (Object.keys (query).length) { if (method !== 'GET') { body = this.json (query); endpart = body; headers['Content-Type'] = 'application/json'; } else { endpoint += '?' + this.urlencode (query); } } const url = this.urls['api'][api] + endpoint; if (api === 'private') { this.checkRequiredCredentials (); const timestamp = this.nonce ().toString (); headers = this.extend ({ 'KC-API-KEY': this.apiKey, 'KC-API-TIMESTAMP': timestamp, 'KC-API-PASSPHRASE': this.password, }, headers); const payload = timestamp + method + endpoint + endpart; const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256', 'base64'); headers['KC-API-SIGN'] = this.decode (signature); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (!response) { this.throwBroadlyMatchedException (this.exceptions['broad'], body, body); return; } // // bad // { "code": "400100", "msg": "validation.createOrder.clientOidIsRequired" } // good // { code: '200000', data: { ... }} // const errorCode = this.safeString (response, 'code'); const message = this.safeString (response, 'msg'); this.throwExactlyMatchedException (this.exceptions['exact'], message, message); this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, message); } }; },{"./base/Exchange":7,"./base/errors":9}],102:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const acx = require ('./acx.js'); const { ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class kuna extends acx { describe () { return this.deepExtend (super.describe (), { 'id': 'kuna', 'name': 'Kuna', 'countries': [ 'UA' ], 'rateLimit': 1000, 'version': 'v2', 'has': { 'CORS': false, 'fetchTickers': true, 'fetchOHLCV': 'emulated', 'fetchOpenOrders': true, 'fetchMyTrades': true, 'withdraw': false, }, 'timeframes': undefined, 'urls': { 'referral': 'https://kuna.io?r=kunaid-gvfihe8az7o4', 'logo': 'https://user-images.githubusercontent.com/1294454/31697638-912824fa-b3c1-11e7-8c36-cf9606eb94ac.jpg', 'api': 'https://kuna.io', 'www': 'https://kuna.io', 'doc': 'https://kuna.io/documents/api', 'fees': 'https://kuna.io/documents/api', }, 'fees': { 'trading': { 'taker': 0.25 / 100, 'maker': 0.25 / 100, }, 'funding': { 'withdraw': { 'UAH': '1%', 'BTC': 0.001, 'BCH': 0.001, 'ETH': 0.01, 'WAVES': 0.01, 'GOL': 0.0, 'GBG': 0.0, // 'RMC': 0.001 BTC // 'ARN': 0.01 ETH // 'R': 0.01 ETH // 'EVR': 0.01 ETH }, 'deposit': { // 'UAH': (amount) => amount * 0.001 + 5 }, }, }, }); } async fetchMarkets (params = {}) { const quotes = [ 'btc', 'eth', 'eurs', 'rub', 'uah', 'usd', 'usdt' ]; const pricePrecisions = { 'UAH': 0, }; const markets = []; const response = await this.publicGetTickers (params); const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; for (let j = 0; j < quotes.length; j++) { const quoteId = quotes[j]; const index = id.indexOf (quoteId); const slice = id.slice (index); if ((index > 0) && (slice === quoteId)) { const baseId = id.replace (quoteId, ''); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': 6, 'price': this.safeInteger (pricePrecisions, quote, 6), }; markets.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'active': undefined, 'info': undefined, }); break; } } } return markets; } async fetchL3OrderBook (symbol, limit = undefined, params = {}) { return await this.fetchOrderBook (symbol, limit, params); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.privateGetOrders (this.extend (request, params)); // todo emulation of fetchClosedOrders, fetchOrders, fetchOrder // with order cache + fetchOpenOrders // as in BTC-e, Liqui, Yobit, DSX, Tidex, WEX return this.parseOrders (response, market, since, limit); } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); let symbol = undefined; if (market) { symbol = market['symbol']; } let side = this.safeString2 (trade, 'side', 'trend'); if (side !== undefined) { const sideMap = { 'ask': 'sell', 'bid': 'buy', }; side = this.safeString (sideMap, side); } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'volume'); const cost = this.safeFloat (trade, 'funds'); const orderId = this.safeString (trade, 'order_id'); const id = this.safeString (trade, 'id'); return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'order': orderId, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.privateGetTradesMy (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limits = undefined, params = {}) { await this.loadMarkets (); const trades = await this.fetchTrades (symbol, since, limits, params); const ohlcvc = this.buildOHLCVC (trades, timeframe, since, limits); const result = []; for (let i = 0; i < ohlcvc.length; i++) { const ohlcv = ohlcvc[i]; result.push ([ ohlcv[0], ohlcv[1], ohlcv[2], ohlcv[3], ohlcv[4], ohlcv[5], ]); } return result; } }; },{"./acx.js":4,"./base/errors":9}],103:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, BadSymbol, InvalidOrder, InsufficientFunds } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class lakebtc extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'lakebtc', 'name': 'LakeBTC', 'countries': [ 'US' ], 'version': 'api_v2', 'rateLimit': 1000, 'has': { 'CORS': true, 'createMarketOrder': false, 'fetchTickers': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/28074120-72b7c38a-6660-11e7-92d9-d9027502281d.jpg', 'api': 'https://api.lakebtc.com', 'www': 'https://www.lakebtc.com', 'doc': [ 'https://www.lakebtc.com/s/api_v2', 'https://www.lakebtc.com/s/api', ], }, 'api': { 'public': { 'get': [ 'bcorderbook', 'bctrades', 'ticker', ], }, 'private': { 'post': [ 'buyOrder', 'cancelOrders', 'getAccountInfo', 'getExternalAccounts', 'getOrders', 'getTrades', 'openOrders', 'sellOrder', ], }, }, 'fees': { 'trading': { 'maker': 0.15 / 100, 'taker': 0.2 / 100, }, }, 'exceptions': { 'broad': { 'Signature': AuthenticationError, 'invalid symbol': BadSymbol, 'Volume doit': InvalidOrder, 'insufficient_balance': InsufficientFunds, }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetTicker (params); const result = []; const keys = Object.keys (response); for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = response[id]; const baseId = id.slice (0, 3); const quoteId = id.slice (3, 6); const base = baseId.toUpperCase (); const quote = quoteId.toUpperCase (); const symbol = base + '/' + quote; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': undefined, 'precision': this.precision, 'limits': this.limits, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGetAccountInfo (params); const balances = this.safeValue (response, 'balance', {}); const result = { 'info': response }; const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balances, currencyId); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; const response = await this.publicGetBcorderbook (this.extend (request, params)); return this.parseOrderBook (response); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker (params); const ids = Object.keys (response); const result = {}; for (let i = 0; i < ids.length; i++) { let symbol = ids[i]; const ticker = response[symbol]; let market = undefined; if (symbol in this.markets_by_id) { market = this.markets_by_id[symbol]; symbol = market['symbol']; } result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const tickers = await this.publicGetTicker (params); return this.parseTicker (tickers[market['id']], market); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); const id = this.safeString (trade, 'tid'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': undefined, 'side': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetBctrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } const method = 'privatePost' + this.capitalize (side) + 'Order'; const market = this.market (symbol); const order = { 'params': [ price, amount, market['id'] ], }; const response = await this[method] (this.extend (order, params)); return { 'info': response, 'id': this.safeString (response, 'id'), }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'params': [ id ], }; return await this.privatePostCancelOrder (this.extend (request, params)); } nonce () { return this.microseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version; if (api === 'public') { url += '/' + path; if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); const nonceAsString = nonce.toString (); const requestId = this.seconds (); let queryParams = ''; if ('params' in params) { const paramsList = params['params']; const stringParams = []; for (let i = 0; i < paramsList.length; i++) { let param = paramsList[i]; if (typeof paramsList !== 'string') { param = param.toString (); } stringParams.push (param); } queryParams = stringParams.join (','); body = { 'method': path, 'params': params['params'], 'id': requestId, }; } else { body = { 'method': path, 'params': '', 'id': requestId, }; } body = this.json (body); let query = [ 'tonce=' + nonceAsString, 'accesskey=' + this.apiKey, 'requestmethod=' + method.toLowerCase (), 'id=' + requestId.toString (), 'method=' + path, 'params=' + queryParams, ]; query = query.join ('&'); const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha1'); const auth = this.encode (this.apiKey + ':' + signature); const signature64 = this.decode (this.stringToBase64 (auth)); headers = { 'Json-Rpc-Tonce': nonceAsString, 'Authorization': 'Basic ' + signature64, 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to the default error handler } // // {"error":"Failed to submit order: invalid symbol"} // {"error":"Failed to submit order: La validation a échoué : Volume doit être supérieur ou égal à 1.0"} // {"error":"Failed to submit order: insufficient_balance"} // const feedback = this.id + ' ' + body; const error = this.safeString (response, 'error'); if (error !== undefined) { this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],104:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, InvalidNonce, OrderNotFound, InvalidOrder, DDoSProtection, BadRequest, AuthenticationError } = require ('./base/errors'); const { ROUND } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class latoken extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'latoken', 'name': 'Latoken', 'countries': [ 'KY' ], // Cayman Islands 'version': 'v1', 'rateLimit': 2000, 'certified': false, 'userAgent': this.userAgents['chrome'], 'has': { 'CORS': false, 'publicAPI': true, 'privateAPI': true, 'cancelOrder': true, 'cancelAllOrders': true, 'createMarketOrder': false, 'createOrder': true, 'fetchBalance': true, 'fetchCanceledOrders': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchMyTrades': true, 'fetchOpenOrders': true, 'fetchOrder': false, 'fetchOrdersByStatus': true, 'fetchOrderBook': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/61511972-24c39f00-aa01-11e9-9f7c-471f1d6e5214.jpg', 'api': 'https://api.latoken.com', 'www': 'https://latoken.com', 'doc': [ 'https://api.latoken.com', ], }, 'api': { 'public': { 'get': [ 'ExchangeInfo/time', 'ExchangeInfo/limits', 'ExchangeInfo/pairs', 'ExchangeInfo/pairs/{currency}', 'ExchangeInfo/pair', 'ExchangeInfo/currencies', 'ExchangeInfo/currencies/{symbol}', 'MarketData/tickers', 'MarketData/ticker/{symbol}', 'MarketData/orderBook/{symbol}', 'MarketData/orderBook/{symbol}/{limit}', 'MarketData/trades/{symbol}', 'MarketData/trades/{symbol}/{limit}', ], }, 'private': { 'get': [ 'Account/balances', 'Account/balances/{currency}', 'Order/status', 'Order/active', 'Order/get_order', 'Order/trades', ], 'post': [ 'Order/new', 'Order/test-order', 'Order/cancel', 'Order/cancel_all', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, }, 'commonCurrencies': { 'MT': 'Monarch', 'TSL': 'Treasure SL', }, 'options': { 'createOrderMethod': 'private_post_order_new', // private_post_order_test_order }, 'exceptions': { 'exact': { 'Signature or ApiKey is not valid': AuthenticationError, 'Request is out of time': InvalidNonce, 'Symbol must be specified': BadRequest, }, 'broad': { 'Request limit reached': DDoSProtection, 'Pair': BadRequest, 'Price needs to be greater than': InvalidOrder, 'Amount needs to be greater than': InvalidOrder, 'The Symbol field is required': InvalidOrder, 'OrderType is not valid': InvalidOrder, 'Side is not valid': InvalidOrder, 'Cancelable order whit': OrderNotFound, 'Order': OrderNotFound, }, }, }); } nonce () { return this.milliseconds (); } async fetchTime (params = {}) { const response = await this.publicGetExchangeInfoTime (params); // // { // "time": "2019-04-18T9:00:00.0Z", // "unixTimeSeconds": 1555578000, // "unixTimeMiliseconds": 1555578000000 // } // return this.safeInteger (response, 'unixTimeMiliseconds'); } async fetchMarkets (params = {}) { const response = await this.publicGetExchangeInfoPairs (params); // // [ // { // "pairId": 502, // "symbol": "LAETH", // "baseCurrency": "LA", // "quotedCurrency": "ETH", // "makerFee": 0.01, // "takerFee": 0.01, // "pricePrecision": 8, // "amountPrecision": 8, // "minQty": 0.1 // } // ] // const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'symbol'); // the exchange shows them inverted const baseId = this.safeString (market, 'baseCurrency'); const quoteId = this.safeString (market, 'quotedCurrency'); const numericId = this.safeInteger (market, 'pairId'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'price': this.safeInteger (market, 'pricePrecision'), 'amount': this.safeInteger (market, 'amountPrecision'), }; const limits = { 'amount': { 'min': this.safeFloat (market, 'minQty'), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }; result.push ({ 'id': id, 'numericId': numericId, 'info': market, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': undefined, // assuming true 'precision': precision, 'limits': limits, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetExchangeInfoCurrencies (params); // // [ // { // "currencyId": 102, // "symbol": "LA", // "name": "Latoken", // "precission": 8, // "type": "ERC20", // "fee": 0.1 // } // ] // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'symbol'); const numericId = this.safeInteger (currency, 'currencyId'); const code = this.safeCurrencyCode (id); const precision = this.safeInteger (currency, 'precission'); const fee = this.safeFloat (currency, 'fee'); const active = undefined; result[code] = { 'id': id, 'numericId': numericId, 'code': code, 'info': currency, 'name': code, 'active': active, 'fee': fee, 'precision': precision, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, }; } return result; } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; let key = 'quote'; const rate = market[takerOrMaker]; let cost = amount * rate; let precision = market['precision']['price']; if (side === 'sell') { cost *= price; } else { key = 'base'; precision = market['precision']['amount']; } cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': parseFloat (cost), }; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccountBalances (params); // // [ // { // "currencyId": 102, // "symbol": "LA", // "name": "Latoken", // "amount": 1054.66, // "available": 900.66, // "frozen": 154, // "pending": 0 // } // ] // const result = { 'info': response, }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'symbol'); const code = this.safeCurrencyCode (currencyId); const frozen = this.safeFloat (balance, 'frozen'); const pending = this.safeFloat (balance, 'pending'); const used = this.sum (frozen, pending); const account = { 'free': this.safeFloat (balance, 'available'), 'used': used, 'total': this.safeFloat (balance, 'amount'), }; result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'limit': 10, }; if (limit !== undefined) { request['limit'] = limit; // default 10, max 100 } const response = await this.publicGetMarketDataOrderBookSymbolLimit (this.extend (request, params)); // // { // "pairId": 502, // "symbol": "LAETH", // "spread": 0.07, // "asks": [ // { "price": 136.3, "quantity": 7.024 } // ], // "bids": [ // { "price": 136.2, "quantity": 6.554 } // ] // } // return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'quantity'); } parseTicker (ticker, market = undefined) { // // { // "pairId":"63b41092-f3f6-4ea4-9e7c-4525ed250dad", // "symbol":"ETHBTC", // "volume":11317.037494474000000000, // "open":0.020033000000000000, // "low":0.019791000000000000, // "high":0.020375000000000000, // "close":0.019923000000000000, // "priceChange":-0.1500 // } // let symbol = undefined; const marketId = this.safeString (ticker, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const open = this.safeFloat (ticker, 'open'); const close = this.safeFloat (ticker, 'close'); let change = undefined; if (open !== undefined && close !== undefined) { change = close - open; } const percentage = this.safeFloat (ticker, 'priceChange'); const timestamp = this.nonce (); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'low': this.safeFloat (ticker, 'low'), 'high': this.safeFloat (ticker, 'high'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': close, 'last': close, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeFloat (ticker, 'volume'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetMarketDataTickerSymbol (this.extend (request, params)); // // { // "pairId": 502, // "symbol": "LAETH", // "volume": 1023314.3202, // "open": 134.82, // "low": 133.95, // "high": 136.22, // "close": 135.12, // "priceChange": 0.22 // } // return this.parseTicker (response, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetMarketDataTickers (params); // // [ // { // "pairId": 502, // "symbol": "LAETH", // "volume": 1023314.3202, // "open": 134.82, // "low": 133.95, // "high": 136.22, // "close": 135.12, // "priceChange": 0.22 // } // ] // const result = {}; for (let i = 0; i < response.length; i++) { const ticker = this.parseTicker (response[i]); const symbol = ticker['symbol']; if (symbols === undefined || this.inArray (symbol, symbols)) { result[symbol] = ticker; } } return result; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // side: 'buy', // price: 0.33634, // amount: 0.01, // timestamp: 1564240008000 // milliseconds // } // // fetchMyTrades (private) // // { // id: '1564223032.892829.3.tg15', // orderId: '1564223032.671436.707548@1379:1', // commission: 0, // side: 'buy', // price: 0.32874, // amount: 0.607, // timestamp: 1564223033 // seconds // } // const type = undefined; let timestamp = this.safeInteger2 (trade, 'timestamp', 'time'); if (timestamp !== undefined) { // 03 Jan 2009 - first block if (timestamp < 1230940800000) { timestamp *= 1000; } } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); const side = this.safeString (trade, 'side'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (trade, 'id'); const orderId = this.safeString (trade, 'orderId'); const feeCost = this.safeFloat (trade, 'commission'); let fee = undefined; if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': undefined, }; } return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': type, 'takerOrMaker': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 50, max 100 } const response = await this.publicGetMarketDataTradesSymbol (this.extend (request, params)); // // { // "pairId":370, // "symbol":"ETHBTC", // "tradeCount":51, // "trades": [ // { // side: 'buy', // price: 0.33634, // amount: 0.01, // timestamp: 1564240008000 // milliseconds // } // ] // } // const trades = this.safeValue (response, 'trades', []); return this.parseTrades (trades, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.privateGetOrderTrades (this.extend (request, params)); // // { // "pairId": 502, // "symbol": "LAETH", // "tradeCount": 1, // "trades": [ // { // id: '1564223032.892829.3.tg15', // orderId: '1564223032.671436.707548@1379:1', // commission: 0, // side: 'buy', // price: 0.32874, // amount: 0.607, // timestamp: 1564223033 // seconds // } // ] // } // const trades = this.safeValue (response, 'trades', []); return this.parseTrades (trades, market, since, limit); } parseOrderStatus (status) { const statuses = { 'active': 'open', 'partiallyFilled': 'open', 'filled': 'closed', 'cancelled': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "orderId":"1563460093.134037.704945@0370:2", // "cliOrdId":"", // "pairId":370, // "symbol":"ETHBTC", // "side":"sell", // "orderType":"limit", // "price":1.0, // "amount":1.0 // } // // cancelOrder, fetchOrder, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders // // { // "orderId": "1555492358.126073.126767@0502:2", // "cliOrdId": "myNewOrder", // "pairId": 502, // "symbol": "LAETH", // "side": "buy", // "orderType": "limit", // "price": 136.2, // "amount": 0.57, // "orderStatus": "partiallyFilled", // "executedAmount": 0.27, // "reaminingAmount": 0.3, // "timeCreated": 155551580736, // "timeFilled": 0 // } // const id = this.safeString (order, 'orderId'); const timestamp = this.safeTimestamp (order, 'timeCreated'); const marketId = this.safeString (order, 'symbol'); let symbol = marketId; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if (market !== undefined) { symbol = market['symbol']; } const side = this.safeString (order, 'side'); const type = this.safeString (order, 'orderType'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount'); const filled = this.safeFloat (order, 'executedAmount'); let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = amount - filled; } } const status = this.parseOrderStatus (this.safeString (order, 'orderStatus')); let cost = undefined; if (filled !== undefined) { if (price !== undefined) { cost = filled * price; } } const timeFilled = this.safeTimestamp (order, 'timeFilled'); let lastTradeTimestamp = undefined; if ((timeFilled !== undefined) && (timeFilled > 0)) { lastTradeTimestamp = timeFilled; } const clientOrderId = this.safeString (order, 'cliOrdId'); return { 'id': id, 'clientOrderId': clientOrderId, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'average': undefined, 'remaining': remaining, 'fee': undefined, 'trades': undefined, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return this.fetchOrdersWithMethod ('private_get_order_active', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return this.fetchOrdersByStatus ('filled', symbol, since, limit, params); } async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return this.fetchOrdersByStatus ('cancelled', symbol, since, limit, params); } async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': status, }; return this.fetchOrdersWithMethod ('private_get_order_status', symbol, since, limit, this.extend (request, params)); } async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrdersWithMethod requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default 100 } const response = await this[method] (this.extend (request, params)); // // [ // { // "orderId": "1555492358.126073.126767@0502:2", // "cliOrdId": "myNewOrder", // "pairId": 502, // "symbol": "LAETH", // "side": "buy", // "orderType": "limit", // "price": 136.2, // "amount": 0.57, // "orderStatus": "partiallyFilled", // "executedAmount": 0.27, // "reaminingAmount": 0.3, // "timeCreated": 155551580736, // "timeFilled": 0 // } // ] // return this.parseOrders (response, market, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; const response = await this.privateGetOrderGetOrder (this.extend (request, params)); // // { // "orderId": "1555492358.126073.126767@0502:2", // "cliOrdId": "myNewOrder", // "pairId": 502, // "symbol": "LAETH", // "side": "buy", // "orderType": "limit", // "price": 136.2, // "amount": 0.57, // "orderStatus": "partiallyFilled", // "executedAmount": 0.27, // "reaminingAmount": 0.3, // "timeCreated": 155551580736, // "timeFilled": 0 // } // return this.parseOrder (response); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type !== 'limit') { throw new ExchangeError (this.id + ' allows limit orders only'); } const request = { 'symbol': this.marketId (symbol), 'side': side, 'price': this.priceToPrecision (symbol, price), 'amount': this.amountToPrecision (symbol, amount), 'orderType': type, }; const method = this.safeString (this.options, 'createOrderMethod', 'private_post_order_new'); const response = await this[method] (this.extend (request, params)); // // { // "orderId":"1563460093.134037.704945@0370:2", // "cliOrdId":"", // "pairId":370, // "symbol":"ETHBTC", // "side":"sell", // "orderType":"limit", // "price":1.0, // "amount":1.0 // } // return this.parseOrder (response); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; const response = await this.privatePostOrderCancel (this.extend (request, params)); // // { // "orderId": "1555492358.126073.126767@0502:2", // "cliOrdId": "myNewOrder", // "pairId": 502, // "symbol": "LAETH", // "side": "buy", // "orderType": "limit", // "price": 136.2, // "amount": 0.57, // "orderStatus": "partiallyFilled", // "executedAmount": 0.27, // "reaminingAmount": 0.3, // "timeCreated": 155551580736, // "timeFilled": 0 // } // return this.parseOrder (response); } async cancelAllOrders (symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument'); } await this.loadMarkets (); const marketId = this.marketId (symbol); const request = { 'symbol': marketId, }; const response = await this.privatePostOrderCancelAll (this.extend (request, params)); // // { // "pairId": 502, // "symbol": "LAETH", // "cancelledOrders": [ // "1555492358.126073.126767@0502:2" // ] // } // const result = []; const canceledOrders = this.safeValue (response, 'cancelledOrders', []); for (let i = 0; i < canceledOrders.length; i++) { const order = this.parseOrder ({ 'symbol': marketId, 'orderId': canceledOrders[i], 'orderStatus': 'canceled', }); result.push (order); } return result; } sign (path, api = 'public', method = 'GET', params = undefined, headers = undefined, body = undefined) { let request = '/api/' + this.version + '/' + this.implodeParams (path, params); let query = this.omit (params, this.extractParams (path)); if (api === 'private') { const nonce = this.nonce (); query = this.extend ({ 'timestamp': nonce, }, query); } const urlencodedQuery = this.urlencode (query); if (Object.keys (query).length) { request += '?' + urlencodedQuery; } if (api === 'private') { this.checkRequiredCredentials (); const signature = this.hmac (this.encode (request), this.encode (this.secret)); headers = { 'X-LA-KEY': this.apiKey, 'X-LA-SIGNATURE': signature, }; if (method === 'POST') { headers['Content-Type'] = 'application/x-www-form-urlencoded'; body = urlencodedQuery; } } const url = this.urls['api'] + request; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (!response) { return; } // // { "message": "Request limit reached!", "details": "Request limit reached. Maximum allowed: 1 per 1s. Please try again in 1 second(s)." } // { "error": { "message": "Pair 370 is not found","errorType":"RequestError","statusCode":400 }} // { "error": { "message": "Signature or ApiKey is not valid","errorType":"RequestError","statusCode":400 }} // { "error": { "message": "Request is out of time", "errorType": "RequestError", "statusCode":400 }} // { "error": { "message": "Price needs to be greater than 0","errorType":"ValidationError","statusCode":400 }} // { "error": { "message": "Side is not valid, Price needs to be greater than 0, Amount needs to be greater than 0, The Symbol field is required., OrderType is not valid","errorType":"ValidationError","statusCode":400 }} // { "error": { "message": "Cancelable order whit ID 1563460289.571254.704945@0370:1 not found","errorType":"RequestError","statusCode":400 }} // { "error": { "message": "Symbol must be specified","errorType":"RequestError","statusCode":400 }} // { "error": { "message": "Order 1563460289.571254.704945@0370:1 is not found","errorType":"RequestError","statusCode":400 }} // const message = this.safeString (response, 'message'); const feedback = this.id + ' ' + body; if (message !== undefined) { this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); } const error = this.safeValue (response, 'error', {}); const errorMessage = this.safeString (error, 'message'); if (errorMessage !== undefined) { this.throwExactlyMatchedException (this.exceptions['exact'], errorMessage, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], errorMessage, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],105:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, DDoSProtection, AuthenticationError, InvalidOrder, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class lbank extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'lbank', 'name': 'LBank', 'countries': [ 'CN' ], 'version': 'v1', 'has': { 'fetchTickers': true, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': false, // status 0 API doesn't work 'fetchClosedOrders': true, }, 'timeframes': { '1m': 'minute1', '5m': 'minute5', '15m': 'minute15', '30m': 'minute30', '1h': 'hour1', '2h': 'hour2', '4h': 'hour4', '6h': 'hour6', '8h': 'hour8', '12h': 'hour12', '1d': 'day1', '1w': 'week1', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/38063602-9605e28a-3302-11e8-81be-64b1e53c4cfb.jpg', 'api': 'https://api.lbank.info', 'www': 'https://www.lbank.info', 'doc': 'https://github.com/LBank-exchange/lbank-official-api-docs', 'fees': 'https://lbankinfo.zendesk.com/hc/en-gb/articles/360012072873-Trading-Fees', 'referral': 'https://www.lbex.io/invite?icode=7QCY', }, 'api': { 'public': { 'get': [ 'currencyPairs', 'ticker', 'depth', 'trades', 'kline', 'accuracy', ], }, 'private': { 'post': [ 'user_info', 'create_order', 'cancel_order', 'orders_info', 'orders_info_history', 'withdraw', 'withdrawCancel', 'withdraws', 'withdrawConfigs', ], }, }, 'fees': { 'trading': { 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, 'funding': { 'withdraw': { 'BTC': undefined, 'ZEC': 0.01, 'ETH': 0.01, 'ETC': 0.01, // 'QTUM': amount => Math.max (0.01, amount * (0.1 / 100)), 'VEN': 10.0, 'BCH': 0.0002, 'SC': 50.0, 'BTM': 20.0, 'NAS': 1.0, 'EOS': 1.0, 'XWC': 5.0, 'BTS': 1.0, 'INK': 10.0, 'BOT': 3.0, 'YOYOW': 15.0, 'TGC': 10.0, 'NEO': 0.0, 'CMT': 20.0, 'SEER': 2000.0, 'FIL': undefined, 'BTG': undefined, }, }, }, 'commonCurrencies': { 'VET_ERC20': 'VEN', }, 'options': { 'cacheSecretAsPem': true, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetAccuracy (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = market['symbol']; const parts = id.split ('_'); let baseId = undefined; let quoteId = undefined; const numParts = parts.length; // lbank will return symbols like "vet_erc20_usdt" if (numParts > 2) { baseId = parts[0] + '_' + parts[1]; quoteId = parts[2]; } else { baseId = parts[0]; quoteId = parts[1]; } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'quantityAccuracy'), 'price': this.safeInteger (market, 'priceAccuracy'), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': id, }); } return result; } parseTicker (ticker, market = undefined) { let symbol = undefined; if (market === undefined) { const marketId = this.safeString (ticker, 'symbol'); if (marketId in this.markets_by_id) { const market = this.marketsById[marketId]; symbol = market['symbol']; } else { const parts = marketId.split ('_'); let baseId = undefined; let quoteId = undefined; const numParts = parts.length; // lbank will return symbols like "vet_erc20_usdt" if (numParts > 2) { baseId = parts[0] + '_' + parts[1]; quoteId = parts[2]; } else { baseId = parts[0]; quoteId = parts[1]; } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } const timestamp = this.safeInteger (ticker, 'timestamp'); const info = ticker; ticker = info['ticker']; const last = this.safeFloat (ticker, 'latest'); const percentage = this.safeFloat (ticker, 'change'); let open = undefined; if (percentage !== undefined) { const relativeChange = this.sum (1, percentage / 100); if (relativeChange > 0) { open = last / this.sum (1, relativeChange); } } let change = undefined; let average = undefined; if (last !== undefined && open !== undefined) { change = last - open; average = this.sum (last, open) / 2; } if (market !== undefined) { symbol = market['symbol']; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': this.safeFloat (ticker, 'turnover'), 'info': info, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTicker (this.extend (request, params)); return this.parseTicker (response, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': 'all', }; const response = await this.publicGetTicker (this.extend (request, params)); const result = {}; for (let i = 0; i < response.length; i++) { const ticker = this.parseTicker (response[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchOrderBook (symbol, limit = 60, params = {}) { await this.loadMarkets (); let size = 60; if (limit !== undefined) { size = Math.min (limit, size); } const request = { 'symbol': this.marketId (symbol), 'size': size, }; const response = await this.publicGetDepth (this.extend (request, params)); return this.parseOrderBook (response); } parseTrade (trade, market = undefined) { let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger (trade, 'date_ms'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = parseFloat (this.costToPrecision (symbol, price * amount)); } } const id = this.safeString (trade, 'tid'); const type = undefined; const side = this.safeString (trade, 'type'); return { 'id': id, 'info': this.safeValue (trade, 'info', trade), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'size': 100, }; if (since !== undefined) { request['time'] = parseInt (since); } if (limit !== undefined) { request['size'] = limit; } const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, ohlcv[1], ohlcv[2], ohlcv[3], ohlcv[4], ohlcv[5], ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = 1000, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (since === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a `since` argument'); } if (limit === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a `limit` argument'); } const request = { 'symbol': market['id'], 'type': this.timeframes[timeframe], 'size': limit, 'time': parseInt (since / 1000), }; const response = await this.publicGetKline (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostUserInfo (params); // // { // "result":"true", // "info":{ // "freeze":{ // "iog":"0.00000000", // "ssc":"0.00000000", // "eon":"0.00000000", // }, // "asset":{ // "iog":"0.00000000", // "ssc":"0.00000000", // "eon":"0.00000000", // }, // "free":{ // "iog":"0.00000000", // "ssc":"0.00000000", // "eon":"0.00000000", // }, // } // } // const result = { 'info': response }; const info = this.safeValue (response, 'info', {}); const free = this.safeValue (info, 'free', {}); const freeze = this.safeValue (info, 'freeze', {}); const asset = this.safeValue (info, 'asset', {}); const currencyIds = Object.keys (free); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (free, currencyId); account['used'] = this.safeFloat (freeze, currencyId); account['total'] = this.safeFloat (asset, currencyId); result[code] = account; } return this.parseBalance (result); } parseOrderStatus (status) { const statuses = { '-1': 'cancelled', // cancelled '0': 'open', // not traded '1': 'open', // partial deal '2': 'closed', // complete deal '4': 'closed', // disposal processing }; return this.safeString (statuses, status); } parseOrder (order, market = undefined) { // // { // "symbol":"eth_btc", // "amount":10.000000, // "create_time":1484289832081, // "price":5000.000000, // "avg_price":5277.301200, // "type":"sell", // "order_id":"ab704110-af0d-48fd-a083-c218f19a4a55", // "deal_amount":10.000000, // "status":2 // } // let symbol = undefined; const responseMarket = this.safeValue (this.marketsById, order['symbol']); if (responseMarket !== undefined) { symbol = responseMarket['symbol']; } else if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeInteger (order, 'create_time'); // Limit Order Request Returns: Order Price // Market Order Returns: cny amount of market order const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount', 0.0); const filled = this.safeFloat (order, 'deal_amount', 0.0); const av_price = this.safeFloat (order, 'avg_price'); let cost = undefined; if (av_price !== undefined) { cost = filled * av_price; } const status = this.parseOrderStatus (this.safeString (order, 'status')); const id = this.safeString (order, 'order_id'); const type = this.safeString (order, 'order_type'); const side = this.safeString (order, 'type'); let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = amount - filled; } } return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': undefined, 'fee': undefined, 'info': this.safeValue (order, 'info', order), 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let order = { 'symbol': market['id'], 'type': side, 'amount': amount, }; if (type === 'market') { order['type'] += '_market'; } else { order['price'] = price; } const response = await this.privatePostCreateOrder (this.extend (order, params)); order = this.omit (order, 'type'); order['order_id'] = response['order_id']; order['type'] = side; order['order_type'] = type; order['create_time'] = this.milliseconds (); order['info'] = response; order = this.parseOrder (order, market); const id = order['id']; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'order_id': id, }; const response = await this.privatePostCancelOrder (this.extend (request, params)); return response; } async fetchOrder (id, symbol = undefined, params = {}) { // Id can be a list of ids delimited by a comma await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'order_id': id, }; const response = await this.privatePostOrdersInfo (this.extend (request, params)); const orders = this.parseOrders (response['orders'], market); const numOrders = orders.length; if (numOrders === 1) { return orders[0]; } else { return orders; } } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (limit === undefined) { limit = 100; } const market = this.market (symbol); const request = { 'symbol': market['id'], 'current_page': 1, 'page_length': limit, }; const response = await this.privatePostOrdersInfoHistory (this.extend (request, params)); return this.parseOrders (response['orders'], undefined, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); const closed = this.filterBy (orders, 'status', 'closed'); const canceled = this.filterBy (orders, 'status', 'cancelled'); // cancelled orders may be partially filled const allOrders = this.arrayConcat (closed, canceled); return this.filterBySymbolSinceLimit (allOrders, symbol, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { // mark and fee are optional params, mark is a note and must be less than 255 characters this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'assetCode': currency['id'], 'amount': amount, 'account': address, }; if (tag !== undefined) { request['memo'] = tag; } const response = this.privatePostWithdraw (this.extend (request, params)); return { 'id': response['id'], 'info': response, }; } convertSecretToPem (secret) { const lineLength = 64; const secretLength = secret.length - 0; let numLines = parseInt (secretLength / lineLength); numLines = this.sum (numLines, 1); let pem = "-----BEGIN PRIVATE KEY-----\n"; // eslint-disable-line for (let i = 0; i < numLines; i++) { const start = i * lineLength; const end = this.sum (start, lineLength); pem += this.secret.slice (start, end) + "\n"; // eslint-disable-line } return pem + '-----END PRIVATE KEY-----'; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); // Every endpoint ends with ".do" url += '.do'; if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const query = this.keysort (this.extend ({ 'api_key': this.apiKey, }, params)); const queryString = this.rawencode (query); const message = this.hash (this.encode (queryString)).toUpperCase (); const cacheSecretAsPem = this.safeValue (this.options, 'cacheSecretAsPem', true); let pem = undefined; if (cacheSecretAsPem) { pem = this.safeValue (this.options, 'pem'); if (pem === undefined) { pem = this.convertSecretToPem (this.secret); this.options['pem'] = pem; } } else { pem = this.convertSecretToPem (this.secret); } const sign = this.binaryToBase64 (this.rsa (message, this.encode (pem), 'RS256')); query['sign'] = sign; body = this.urlencode (query); headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); const success = this.safeString (response, 'result'); if (success === 'false') { const errorCode = this.safeString (response, 'error_code'); const message = this.safeString ({ '10000': 'Internal error', '10001': 'The required parameters can not be empty', '10002': 'verification failed', '10003': 'Illegal parameters', '10004': 'User requests are too frequent', '10005': 'Key does not exist', '10006': 'user does not exist', '10007': 'Invalid signature', '10008': 'This currency pair is not supported', '10009': 'Limit orders can not be missing orders and the number of orders', '10010': 'Order price or order quantity must be greater than 0', '10011': 'Market orders can not be missing the amount of the order', '10012': 'market sell orders can not be missing orders', '10013': 'is less than the minimum trading position 0.001', '10014': 'Account number is not enough', '10015': 'The order type is wrong', '10016': 'Account balance is not enough', '10017': 'Abnormal server', '10018': 'order inquiry can not be more than 50 less than one', '10019': 'withdrawal orders can not be more than 3 less than one', '10020': 'less than the minimum amount of the transaction limit of 0.001', '10022': 'Insufficient key authority', }, errorCode, this.json (response)); const ErrorClass = this.safeValue ({ '10002': AuthenticationError, '10004': DDoSProtection, '10005': AuthenticationError, '10006': AuthenticationError, '10007': AuthenticationError, '10009': InvalidOrder, '10010': InvalidOrder, '10011': InvalidOrder, '10012': InvalidOrder, '10013': InvalidOrder, '10014': InvalidOrder, '10015': InvalidOrder, '10016': InvalidOrder, '10022': AuthenticationError, }, errorCode, ExchangeError); throw new ErrorClass (message); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],106:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, InvalidNonce, OrderNotFound, InvalidOrder, InsufficientFunds, AuthenticationError, DDoSProtection, NotSupported } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class liquid extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'liquid', 'name': 'Liquid', 'countries': [ 'JP', 'CN', 'TW' ], 'version': '2', 'rateLimit': 1000, 'has': { 'CORS': false, 'fetchCurrencies': true, 'fetchTickers': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/45798859-1a872600-bcb4-11e8-8746-69291ce87b04.jpg', 'api': 'https://api.liquid.com', 'www': 'https://www.liquid.com', 'doc': [ 'https://developers.liquid.com', ], 'fees': 'https://help.liquid.com/getting-started-with-liquid/the-platform/fee-structure', 'referral': 'https://www.liquid.com?affiliate=SbzC62lt30976', }, 'api': { 'public': { 'get': [ 'currencies', 'products', 'products/{id}', 'products/{id}/price_levels', 'executions', 'ir_ladders/{currency}', 'fees', // add fetchFees, fetchTradingFees, fetchFundingFees ], }, 'private': { 'get': [ 'accounts/balance', 'accounts/main_asset', 'accounts/{id}', 'accounts/{currency}/reserved_balance_details', 'crypto_accounts', // add fetchAccounts 'crypto_withdrawals', // add fetchWithdrawals 'executions/me', 'fiat_accounts', // add fetchAccounts 'fund_infos', // add fetchDeposits 'loan_bids', 'loans', 'orders', 'orders/{id}', 'orders/{id}/trades', // add fetchOrderTrades 'trades', 'trades/{id}/loans', 'trading_accounts', 'trading_accounts/{id}', 'transactions', 'withdrawals', // add fetchWithdrawals ], 'post': [ 'crypto_withdrawals', 'fund_infos', 'fiat_accounts', 'loan_bids', 'orders', 'withdrawals', ], 'put': [ 'crypto_withdrawal/{id}/cancel', 'loan_bids/{id}/close', 'loans/{id}', 'orders/{id}', // add editOrder 'orders/{id}/cancel', 'trades/{id}', 'trades/{id}/adjust_margin', 'trades/{id}/close', 'trades/close_all', 'trading_accounts/{id}', 'withdrawals/{id}/cancel', ], }, }, 'exceptions': { 'API rate limit exceeded. Please retry after 300s': DDoSProtection, 'API Authentication failed': AuthenticationError, 'Nonce is too small': InvalidNonce, 'Order not found': OrderNotFound, 'Can not update partially filled order': InvalidOrder, 'Can not update non-live order': OrderNotFound, 'not_enough_free_balance': InsufficientFunds, 'must_be_positive': InvalidOrder, 'less_than_order_size': InvalidOrder, }, 'commonCurrencies': { 'WIN': 'WCOIN', 'HOT': 'HOT Token', }, 'options': { 'cancelOrderException': true, }, }); } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencies (params); // // [ // { // currency_type: 'fiat', // currency: 'USD', // symbol: '$', // assets_precision: 2, // quoting_precision: 5, // minimum_withdrawal: '15.0', // withdrawal_fee: 5, // minimum_fee: null, // minimum_order_quantity: null, // display_precision: 2, // depositable: true, // withdrawable: true, // discount_fee: 0.5, // }, // ] // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'currency'); const code = this.safeCurrencyCode (id); const active = currency['depositable'] && currency['withdrawable']; const amountPrecision = this.safeInteger (currency, 'display_precision'); const pricePrecision = this.safeInteger (currency, 'quoting_precision'); const precision = Math.max (amountPrecision, pricePrecision); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': code, 'active': active, 'fee': this.safeFloat (currency, 'withdrawal_fee'), 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -amountPrecision), 'max': Math.pow (10, amountPrecision), }, 'price': { 'min': Math.pow (10, -pricePrecision), 'max': Math.pow (10, pricePrecision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'minimum_withdrawal'), 'max': undefined, }, }, }; } return result; } async fetchMarkets (params = {}) { const spot = await this.publicGetProducts (params); // // [ // { // id: '7', // product_type: 'CurrencyPair', // code: 'CASH', // name: ' CASH Trading', // market_ask: 8865.79147, // market_bid: 8853.95988, // indicator: 1, // currency: 'SGD', // currency_pair_code: 'BTCSGD', // symbol: 'S$', // btc_minimum_withdraw: null, // fiat_minimum_withdraw: null, // pusher_channel: 'product_cash_btcsgd_7', // taker_fee: 0, // maker_fee: 0, // low_market_bid: '8803.25579', // high_market_ask: '8905.0', // volume_24h: '15.85443468', // last_price_24h: '8807.54625', // last_traded_price: '8857.77206', // last_traded_quantity: '0.00590974', // quoted_currency: 'SGD', // base_currency: 'BTC', // disabled: false, // }, // ] // const perpetual = await this.publicGetProducts ({ 'perpetual': '1' }); // // [ // { // "id": "603", // "product_type": "Perpetual", // "code": "CASH", // "name": null, // "market_ask": "1143900", // "market_bid": "1143250", // "currency": "JPY", // "currency_pair_code": "P-BTCJPY", // "pusher_channel": "product_cash_p-btcjpy_603", // "taker_fee": "0.0", // "maker_fee": "0.0", // "low_market_bid": "1124450.0", // "high_market_ask": "1151750.0", // "volume_24h": "0.1756", // "last_price_24h": "1129850.0", // "last_traded_price": "1144700.0", // "last_traded_quantity": "0.014", // "quoted_currency": "JPY", // "base_currency": "P-BTC", // "tick_size": "50.0", // "perpetual_enabled": true, // "index_price": "1142636.03935", // "mark_price": "1143522.18417", // "funding_rate": "0.00033", // "fair_price": "1143609.31009", // "timestamp": "1581558659.195353100", // }, // ] // const currencies = await this.fetchCurrencies (); const currenciesByCode = this.indexBy (currencies, 'code'); const result = []; const markets = this.arrayConcat (spot, perpetual); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'id'); const baseId = this.safeString (market, 'base_currency'); const quoteId = this.safeString (market, 'quoted_currency'); const productType = this.safeString (market, 'product_type'); let type = 'spot'; let spot = true; let swap = false; if (productType === 'Perpetual') { spot = false; swap = true; type = 'swap'; } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); let symbol = undefined; if (swap) { symbol = this.safeString (market, 'currency_pair_code'); } else { symbol = base + '/' + quote; } const maker = this.safeFloat (market, 'maker_fee'); const taker = this.safeFloat (market, 'taker_fee'); const disabled = this.safeValue (market, 'disabled', false); const active = !disabled; const baseCurrency = this.safeValue (currenciesByCode, base); const quoteCurrency = this.safeValue (currenciesByCode, quote); const precision = { 'amount': 8, 'price': 8, }; let minAmount = undefined; if (baseCurrency !== undefined) { minAmount = this.safeFloat (baseCurrency['info'], 'minimum_order_quantity'); // precision['amount'] = this.safeInteger (baseCurrency['info'], 'quoting_precision'); } let minPrice = undefined; if (quoteCurrency !== undefined) { precision['price'] = this.safeInteger (quoteCurrency['info'], 'quoting_precision'); minPrice = Math.pow (10, -precision['price']); } let minCost = undefined; if (minPrice !== undefined) { if (minAmount !== undefined) { minCost = minPrice * minAmount; } } const limits = { 'amount': { 'min': minAmount, 'max': undefined, }, 'price': { 'min': minPrice, 'max': undefined, }, 'cost': { 'min': minCost, 'max': undefined, }, }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'type': type, 'spot': spot, 'swap': swap, 'maker': maker, 'taker': taker, 'limits': limits, 'precision': precision, 'active': active, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccountsBalance (params); // // [ // {"currency":"USD","balance":"0.0"}, // {"currency":"BTC","balance":"0.0"}, // {"currency":"ETH","balance":"0.1651354"} // ] // const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balance, 'balance'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const response = await this.publicGetProductsIdPriceLevels (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'buy_price_levels', 'sell_price_levels'); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let last = undefined; if ('last_traded_price' in ticker) { if (ticker['last_traded_price']) { const length = ticker['last_traded_price'].length; if (length > 0) { last = this.safeFloat (ticker, 'last_traded_price'); } } } let symbol = undefined; if (market === undefined) { const marketId = this.safeString (ticker, 'id'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const baseId = this.safeString (ticker, 'base_currency'); const quoteId = this.safeString (ticker, 'quoted_currency'); if (symbol in this.markets) { market = this.markets[symbol]; } else { symbol = this.safeCurrencyCode (baseId) + '/' + this.safeCurrencyCode (quoteId); } } } if (market !== undefined) { symbol = market['symbol']; } let change = undefined; let percentage = undefined; let average = undefined; const open = this.safeFloat (ticker, 'last_price_24h'); if (open !== undefined && last !== undefined) { change = last - open; average = this.sum (last, open) / 2; if (open > 0) { percentage = change / open * 100; } } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high_market_ask'), 'low': this.safeFloat (ticker, 'low_market_bid'), 'bid': this.safeFloat (ticker, 'market_bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'market_ask'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': this.safeFloat (ticker, 'volume_24h'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetProducts (params); const result = {}; for (let i = 0; i < response.length; i++) { const ticker = this.parseTicker (response[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; const response = await this.publicGetProductsId (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { // { id: 12345, // quantity: "6.789", // price: "98765.4321", // taker_side: "sell", // created_at: 1512345678, // my_side: "buy" } const timestamp = this.safeTimestamp (trade, 'created_at'); const orderId = this.safeString (trade, 'order_id'); // 'taker_side' gets filled for both fetchTrades and fetchMyTrades const takerSide = this.safeString (trade, 'taker_side'); // 'my_side' gets filled for fetchMyTrades only and may differ from 'taker_side' const mySide = this.safeString (trade, 'my_side'); const side = (mySide !== undefined) ? mySide : takerSide; let takerOrMaker = undefined; if (mySide !== undefined) { takerOrMaker = (takerSide === mySide) ? 'taker' : 'maker'; } let cost = undefined; const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'quantity'); if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } const id = this.safeString (trade, 'id'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'product_id': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { // timestamp should be in seconds, whereas we use milliseconds in since and everywhere request['timestamp'] = parseInt (since / 1000); } const response = await this.publicGetExecutions (this.extend (request, params)); const result = (since !== undefined) ? response : response['models']; return this.parseTrades (result, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); // the `with_details` param is undocumented - it adds the order_id to the results const request = { 'product_id': market['id'], 'with_details': true, }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetExecutionsMe (this.extend (request, params)); return this.parseTrades (response['models'], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_type': type, 'product_id': this.marketId (symbol), 'side': side, 'quantity': this.amountToPrecision (symbol, amount), }; if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostOrders (this.extend (request, params)); // // { // "id": 2157474, // "order_type": "limit", // "quantity": "0.01", // "disc_quantity": "0.0", // "iceberg_total_quantity": "0.0", // "side": "sell", // "filled_quantity": "0.0", // "price": "500.0", // "created_at": 1462123639, // "updated_at": 1462123639, // "status": "live", // "leverage_level": 1, // "source_exchange": "QUOINE", // "product_id": 1, // "product_code": "CASH", // "funding_currency": "USD", // "currency_pair_code": "BTCUSD", // "order_fee": "0.0" // } // return this.parseOrder (response); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privatePutOrdersIdCancel (this.extend (request, params)); const order = this.parseOrder (response); if (order['status'] === 'closed') { if (this.options['cancelOrderException']) { throw new OrderNotFound (this.id + ' order closed already: ' + this.json (response)); } } return order; } async editOrder (id, symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (price === undefined) { throw new ArgumentsRequired (this.id + ' editOrder requires the price argument'); } const request = { 'order': { 'quantity': this.amountToPrecision (symbol, amount), 'price': this.priceToPrecision (symbol, price), }, 'id': id, }; const response = await this.privatePutOrdersId (this.extend (request, params)); return this.parseOrder (response); } parseOrderStatus (status) { const statuses = { 'live': 'open', 'filled': 'closed', 'cancelled': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder // // { // "id": 2157474, // "order_type": "limit", // "quantity": "0.01", // "disc_quantity": "0.0", // "iceberg_total_quantity": "0.0", // "side": "sell", // "filled_quantity": "0.0", // "price": "500.0", // "created_at": 1462123639, // "updated_at": 1462123639, // "status": "live", // "leverage_level": 1, // "source_exchange": "QUOINE", // "product_id": 1, // "product_code": "CASH", // "funding_currency": "USD", // "currency_pair_code": "BTCUSD", // "order_fee": "0.0" // } // // fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders // // { // "id": 2157479, // "order_type": "limit", // "quantity": "0.01", // "disc_quantity": "0.0", // "iceberg_total_quantity": "0.0", // "side": "sell", // "filled_quantity": "0.01", // "price": "500.0", // "created_at": 1462123639, // "updated_at": 1462123639, // "status": "filled", // "leverage_level": 2, // "source_exchange": "QUOINE", // "product_id": 1, // "product_code": "CASH", // "funding_currency": "USD", // "currency_pair_code": "BTCUSD", // "order_fee": "0.0", // "executions": [ // { // "id": 4566133, // "quantity": "0.01", // "price": "500.0", // "taker_side": "buy", // "my_side": "sell", // "created_at": 1465396785 // } // ] // } // const orderId = this.safeString (order, 'id'); const timestamp = this.safeTimestamp (order, 'created_at'); const marketId = this.safeString (order, 'product_id'); market = this.safeValue (this.markets_by_id, marketId); const status = this.parseOrderStatus (this.safeString (order, 'status')); const amount = this.safeFloat (order, 'quantity'); let filled = this.safeFloat (order, 'filled_quantity'); const price = this.safeFloat (order, 'price'); let symbol = undefined; let feeCurrency = undefined; if (market !== undefined) { symbol = market['symbol']; feeCurrency = market['quote']; } const type = this.safeString (order, 'order_type'); let tradeCost = 0; let tradeFilled = 0; let average = this.safeFloat (order, 'average_price'); const trades = this.parseTrades (this.safeValue (order, 'executions', []), market, undefined, undefined, { 'order': orderId, 'type': type, }); const numTrades = trades.length; for (let i = 0; i < numTrades; i++) { // php copies values upon assignment, but not references them // todo rewrite this (shortly) const trade = trades[i]; trade['order'] = orderId; trade['type'] = type; tradeFilled = this.sum (tradeFilled, trade['amount']); tradeCost = this.sum (tradeCost, trade['cost']); } let cost = undefined; let lastTradeTimestamp = undefined; if (numTrades > 0) { lastTradeTimestamp = trades[numTrades - 1]['timestamp']; if (!average && (tradeFilled > 0)) { average = tradeCost / tradeFilled; } if (cost === undefined) { cost = tradeCost; } if (filled === undefined) { filled = tradeFilled; } } let remaining = undefined; if (amount !== undefined && filled !== undefined) { remaining = amount - filled; } const side = this.safeString (order, 'side'); return { 'id': orderId, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'type': type, 'status': status, 'symbol': symbol, 'side': side, 'price': price, 'amount': amount, 'filled': filled, 'cost': cost, 'remaining': remaining, 'average': average, 'trades': trades, 'fee': { 'currency': feeCurrency, 'cost': this.safeFloat (order, 'order_fee'), }, 'info': order, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privateGetOrdersId (this.extend (request, params)); return this.parseOrder (response); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = { // 'funding_currency': market['quoteId'], // filter orders based on "funding" currency (quote currency) // 'product_id': market['id'], // 'status': 'live', // 'filled', 'cancelled' // 'trading_type': 'spot', // 'margin', 'cfd' 'with_details': 1, // return full order details including executions }; if (symbol !== undefined) { market = this.market (symbol); request['product_id'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetOrders (this.extend (request, params)); // // { // "models": [ // { // "id": 2157474, // "order_type": "limit", // "quantity": "0.01", // "disc_quantity": "0.0", // "iceberg_total_quantity": "0.0", // "side": "sell", // "filled_quantity": "0.0", // "price": "500.0", // "created_at": 1462123639, // "updated_at": 1462123639, // "status": "live", // "leverage_level": 1, // "source_exchange": "QUOINE", // "product_id": 1, // "product_code": "CASH", // "funding_currency": "USD", // "currency_pair_code": "BTCUSD", // "order_fee": "0.0", // "executions": [], // optional // } // ], // "current_page": 1, // "total_pages": 1 // } // const orders = this.safeValue (response, 'models', []); return this.parseOrders (orders, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': 'live' }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': 'filled' }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { // 'auth_code': '', // optional 2fa code 'currency': currency['id'], 'address': address, 'amount': this.currencyToPrecision (code, amount), // 'payment_id': tag, // for XRP only // 'memo_type': 'text', // 'text', 'id' or 'hash', for XLM only // 'memo_value': tag, // for XLM only }; if (tag !== undefined) { if (code === 'XRP') { request['payment_id'] = tag; } else if (code === 'XLM') { request['memo_type'] = 'text'; // overrideable via params request['memo_value'] = tag; } else { throw new NotSupported (this.id + ' withdraw() only supports a tag along the address for XRP or XLM'); } } const response = await this.privatePostCryptoWithdrawals (this.extend (request, params)); // // { // "id": 1353, // "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", // "amount": 1.0, // "state": "pending", // "currency": "BTC", // "withdrawal_fee": 0.0, // "created_at": 1568016450, // "updated_at": 1568016450, // "payment_id": null // } // return this.parseTransaction (response, currency); } parseTransactionStatus (status) { const statuses = { 'pending': 'pending', 'cancelled': 'canceled', 'approved': 'ok', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // withdraw // // { // "id": 1353, // "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", // "amount": 1.0, // "state": "pending", // "currency": "BTC", // "withdrawal_fee": 0.0, // "created_at": 1568016450, // "updated_at": 1568016450, // "payment_id": null // } // // fetchDeposits, fetchWithdrawals // // ... // const id = this.safeString (transaction, 'id'); const address = this.safeString (transaction, 'address'); const tag = this.safeString2 (transaction, 'payment_id', 'memo_value'); const txid = undefined; const currencyId = this.safeString (transaction, 'asset'); const code = this.safeCurrencyCode (currencyId, currency); const timestamp = this.safeTimestamp (transaction, 'created_at'); const updated = this.safeTimestamp (transaction, 'updated_at'); const type = 'withdrawal'; const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); const amount = this.safeFloat (transaction, 'amount'); return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'address': address, 'tag': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': updated, 'fee': undefined, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); headers = { 'X-Quoine-API-Version': this.version, 'Content-Type': 'application/json', }; if (api === 'private') { this.checkRequiredCredentials (); if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (Object.keys (query).length) { body = this.json (query); } const nonce = this.nonce (); const request = { 'path': url, 'nonce': nonce, 'token_id': this.apiKey, 'iat': Math.floor (nonce / 1000), // issued at }; headers['X-Quoine-Auth'] = this.jwt (request, this.encode (this.secret)); } else { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } url = this.urls['api'] + url; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (code >= 200 && code < 300) { return; } if (code === 401) { // expected non-json response this.throwExactlyMatchedException (this.exceptions, body, body); return; } if (code === 429) { throw new DDoSProtection (this.id + ' ' + body); } if (response === undefined) { return; } const feedback = this.id + ' ' + body; const message = this.safeString (response, 'message'); const errors = this.safeValue (response, 'errors'); if (message !== undefined) { // // { "message": "Order not found" } // this.throwExactlyMatchedException (this.exceptions, message, feedback); } else if (errors !== undefined) { // // { "errors": { "user": ["not_enough_free_balance"] }} // { "errors": { "quantity": ["less_than_order_size"] }} // { "errors": { "order": ["Can not update partially filled order"] }} // const types = Object.keys (errors); for (let i = 0; i < types.length; i++) { const type = types[i]; const errorMessages = errors[type]; for (let j = 0; j < errorMessages.length; j++) { const message = errorMessages[j]; this.throwExactlyMatchedException (this.exceptions, message, feedback); } } } else { throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9}],107:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, AuthenticationError, NotSupported, InvalidOrder, OrderNotFound, ExchangeNotAvailable, RateLimitExceeded, InsufficientFunds } = require ('./base/errors'); const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class livecoin extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'livecoin', 'name': 'LiveCoin', 'countries': [ 'US', 'UK', 'RU' ], 'rateLimit': 1000, 'userAgent': this.userAgents['chrome'], 'has': { 'fetchDepositAddress': true, 'fetchDeposits': true, 'CORS': false, 'fetchTickers': true, 'fetchCurrencies': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'fetchOrders': true, 'fetchOrder': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchWithdrawals': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27980768-f22fc424-638a-11e7-89c9-6010a54ff9be.jpg', 'api': 'https://api.livecoin.net', 'www': 'https://www.livecoin.net', 'doc': 'https://www.livecoin.net/api?lang=en', 'referral': 'https://livecoin.net/?from=Livecoin-CQ1hfx44', }, 'api': { 'public': { 'get': [ 'exchange/all/order_book', 'exchange/last_trades', 'exchange/maxbid_minask', 'exchange/order_book', 'exchange/restrictions', 'exchange/ticker', // omit params to get all tickers at once 'info/coinInfo', ], }, 'private': { 'get': [ 'exchange/client_orders', 'exchange/order', 'exchange/trades', 'exchange/commission', 'exchange/commissionCommonInfo', 'payment/balances', 'payment/balance', 'payment/get/address', 'payment/history/size', 'payment/history/transactions', ], 'post': [ 'exchange/buylimit', 'exchange/buymarket', 'exchange/cancellimit', 'exchange/selllimit', 'exchange/sellmarket', 'payment/out/capitalist', 'payment/out/card', 'payment/out/coin', 'payment/out/okpay', 'payment/out/payeer', 'payment/out/perfectmoney', 'payment/voucher/amount', 'payment/voucher/make', 'payment/voucher/redeem', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.18 / 100, 'taker': 0.18 / 100, }, }, 'commonCurrencies': { 'BTCH': 'Bithash', 'CPC': 'Capricoin', 'CPT': 'Cryptos', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 'EDR': 'E-Dinar Coin', // conflicts with EDR for Endor Protocol and EDRCoin 'eETT': 'EETT', 'FirstBlood': '1ST', 'FORTYTWO': '42', 'LEO': 'LeoCoin', 'ORE': 'Orectic', 'PLN': 'Plutaneum', // conflict with Polish Zloty 'RUR': 'RUB', 'SCT': 'SpaceCoin', 'TPI': 'ThaneCoin', 'WAX': 'WAXP', 'wETT': 'WETT', 'XBT': 'Bricktox', }, 'exceptions': { 'exact': { '1': ExchangeError, '10': AuthenticationError, '100': ExchangeError, // invalid parameters '101': AuthenticationError, '102': AuthenticationError, '103': InvalidOrder, // invalid currency '104': InvalidOrder, // invalid amount '105': InvalidOrder, // unable to block funds '11': AuthenticationError, '12': AuthenticationError, '2': AuthenticationError, // "User not found" '20': AuthenticationError, '30': AuthenticationError, '31': NotSupported, '32': ExchangeError, '429': RateLimitExceeded, '503': ExchangeNotAvailable, }, 'broad': { 'insufficient funds': InsufficientFunds, // https://github.com/ccxt/ccxt/issues/5749 'NOT FOUND': OrderNotFound, 'Cannot find order': OrderNotFound, 'Minimal amount is': InvalidOrder, }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetExchangeTicker (params); const restrictions = await this.publicGetExchangeRestrictions (); const restrictionsById = this.indexBy (restrictions['restrictions'], 'currencyPair'); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'symbol'); const [ baseId, quoteId ] = id.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const coinRestrictions = this.safeValue (restrictionsById, symbol); const precision = { 'price': 5, 'amount': 8, 'cost': 8, }; const limits = { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']), }, }; if (coinRestrictions) { precision['price'] = this.safeInteger (coinRestrictions, 'priceScale', 5); limits['amount']['min'] = this.safeFloat (coinRestrictions, 'minLimitQuantity', limits['amount']['min']); } limits['price'] = { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetInfoCoinInfo (params); const currencies = this.safeValue (response, 'info'); let result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'symbol'); // todo: will need to rethink the fees // to add support for multiple withdrawal/deposit methods and // differentiated fees for each particular method const code = this.safeCurrencyCode (id); const precision = 8; // default precision, todo: fix "magic constants" const walletStatus = this.safeString (currency, 'walletStatus'); const active = (walletStatus === 'normal'); const name = this.safeString (currency, 'name'); const fee = this.safeFloat (currency, 'withdrawFee'); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': name, 'active': active, 'fee': fee, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (currency, 'minOrderAmount'), 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': this.safeFloat (currency, 'minOrderAmount'), 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'minWithdrawAmount'), 'max': Math.pow (10, precision), }, 'deposit': { 'min': this.safeFloat (currency, 'minDepositAmount'), 'max': undefined, }, }, }; } result = this.appendFiatCurrencies (result); return result; } appendFiatCurrencies (result) { const precision = 8; const defaults = { 'info': undefined, 'active': true, 'fee': undefined, 'precision': precision, 'limits': { 'withdraw': { 'min': undefined, 'max': undefined }, 'deposit': { 'min': undefined, 'max': undefined }, 'amount': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, }, 'id': undefined, 'code': undefined, 'name': undefined, }; const currencies = [ { 'id': 'USD', 'code': 'USD', 'name': 'US Dollar' }, { 'id': 'EUR', 'code': 'EUR', 'name': 'Euro' }, // { 'id': 'RUR', 'code': 'RUB', 'name': 'Russian ruble' }, ]; currencies.push ({ 'id': 'RUR', 'code': this.safeCurrencyCode ('RUR'), 'name': 'Russian ruble', }); for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const code = currency['code']; result[code] = this.extend (defaults, currency); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetPaymentBalances (params); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); let account = undefined; if (code in result) { account = result[code]; } else { account = this.account (); } if (balance['type'] === 'total') { account['total'] = this.safeFloat (balance, 'value'); } if (balance['type'] === 'available') { account['free'] = this.safeFloat (balance, 'value'); } if (balance['type'] === 'trade') { account['used'] = this.safeFloat (balance, 'value'); } result[code] = account; } return this.parseBalance (result); } async fetchTradingFees (params = {}) { await this.loadMarkets (); const response = await this.privateGetExchangeCommissionCommonInfo (params); const commission = this.safeFloat (response, 'commission'); return { 'info': response, 'maker': commission, 'taker': commission, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'currencyPair': this.marketId (symbol), 'groupByPrice': 'false', }; if (limit !== undefined) { request['depth'] = limit; // 100 } const response = await this.publicGetExchangeOrderBook (this.extend (request, params)); const timestamp = this.safeInteger (response, 'timestamp'); return this.parseOrderBook (response, timestamp); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market) { symbol = market['symbol']; } const vwap = this.safeFloat (ticker, 'vwap'); const baseVolume = this.safeFloat (ticker, 'volume'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'best_bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'best_ask'), 'askVolume': undefined, 'vwap': this.safeFloat (ticker, 'vwap'), 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetExchangeTicker (params); const tickers = this.indexBy (response, 'symbol'); const ids = Object.keys (tickers); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = this.markets_by_id[id]; const symbol = market['symbol']; const ticker = tickers[id]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPair': market['id'], }; const ticker = await this.publicGetExchangeTicker (this.extend (request, params)); return this.parseTicker (ticker, market); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "time": 1409935047, // "id": 99451, // "price": 350, // "quantity": 2.85714285, // "type": "BUY" // } // // fetchMyTrades (private) // // { // "datetime": 1435844369, // "id": 30651619, // "type": "sell", // "symbol": "BTC/EUR", // "price": 230, // "quantity": 0.1, // "commission": 0, // "clientorderid": 1472837650 // } const timestamp = this.safeTimestamp2 (trade, 'time', 'datetime'); let fee = undefined; const feeCost = this.safeFloat (trade, 'commission'); if (feeCost !== undefined) { const feeCurrency = market ? market['quote'] : undefined; fee = { 'cost': feeCost, 'currency': feeCurrency, }; } const orderId = this.safeString (trade, 'clientorderid'); const id = this.safeString (trade, 'id'); const side = this.safeStringLower (trade, 'type'); const amount = this.safeFloat (trade, 'quantity'); const price = this.safeFloat (trade, 'price'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { const [ baseId, quoteId ] = marketId.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'currencyPair': market['id'], // 'orderDesc': 'true', // or 'false', if true then new orders will be first, otherwise old orders will be first. // 'offset': 0, // page offset, position of the first item on the page }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['currencyPair'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetExchangeTrades (this.extend (request, params)); // // [ // { // "datetime": 1435844369, // "id": 30651619, // "type": "sell", // "symbol": "BTC/EUR", // "price": 230, // "quantity": 0.1, // "commission": 0, // "clientorderid": 1472837650 // }, // { // "datetime": 1435844356, // "id": 30651618, // "type": "sell", // "symbol": "BTC/EUR", // "price": 230, // "quantity": 0.2, // "commission": 0.092, // "clientorderid": 1472837651 // } // ] // return this.parseTrades (response, market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPair': market['id'], }; const response = await this.publicGetExchangeLastTrades (this.extend (request, params)); // // [ // { // "time": 1409935047, // "id": 99451, // "price": 350, // "quantity": 2.85714285, // "type": "BUY" // }, // { // "time": 1409934792, // "id": 99450, // "price": 350, // "quantity": 0.57142857, // "type": "SELL" // } // ] // return this.parseTrades (response, market, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; const response = await this.privateGetExchangeOrder (this.extend (request, params)); return this.parseOrder (response); } parseOrderStatus (status) { const statuses = { 'OPEN': 'open', 'PARTIALLY_FILLED': 'open', 'EXECUTED': 'closed', 'CANCELLED': 'canceled', 'PARTIALLY_FILLED_AND_CANCELLED': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { let timestamp = undefined; if ('lastModificationTime' in order) { timestamp = this.safeString (order, 'lastModificationTime'); if (timestamp !== undefined) { if (timestamp.indexOf ('T') >= 0) { timestamp = this.parse8601 (timestamp); } else { timestamp = this.safeInteger (order, 'lastModificationTime'); } } } // TODO currently not supported by livecoin // let trades = this.parseTrades (order['trades'], market, since, limit); const trades = undefined; const status = this.parseOrderStatus (this.safeString2 (order, 'status', 'orderStatus')); let symbol = undefined; if (market === undefined) { let marketId = this.safeString (order, 'currencyPair'); marketId = this.safeString (order, 'symbol', marketId); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } let type = this.safeStringLower (order, 'type'); let side = undefined; if (type !== undefined) { const orderType = type.split ('_'); type = orderType[0]; side = orderType[1]; } const price = this.safeFloat (order, 'price'); // of the next two lines the latter overrides the former, if present in the order structure let remaining = this.safeFloat (order, 'remainingQuantity'); remaining = this.safeFloat (order, 'remaining_quantity', remaining); const amount = this.safeFloat (order, 'quantity', remaining); let filled = undefined; if (remaining !== undefined) { filled = amount - remaining; } let cost = undefined; if (filled !== undefined && price !== undefined) { cost = filled * price; } const feeRate = this.safeFloat (order, 'commission_rate'); let feeCost = undefined; if (cost !== undefined && feeRate !== undefined) { feeCost = cost * feeRate; } let feeCurrency = undefined; if (market !== undefined) { symbol = market['symbol']; feeCurrency = market['quote']; } return { 'info': order, 'id': order['id'], 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'filled': filled, 'remaining': remaining, 'trades': trades, 'fee': { 'cost': feeCost, 'currency': feeCurrency, 'rate': feeRate, }, 'average': undefined, }; } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['currencyPair'] = market['id']; } if (since !== undefined) { request['issuedFrom'] = parseInt (since); } if (limit !== undefined) { request['endRow'] = limit - 1; } const response = await this.privateGetExchangeClientOrders (this.extend (request, params)); const result = []; let rawOrders = []; if (response['data']) { rawOrders = response['data']; } for (let i = 0; i < rawOrders.length; i++) { const order = rawOrders[i]; result.push (this.parseOrder (order, market)); } return this.sortBy (result, 'timestamp'); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'openClosed': 'OPEN', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'openClosed': 'CLOSED', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const method = 'privatePostExchange' + this.capitalize (side) + type; const market = this.market (symbol); const request = { 'quantity': this.amountToPrecision (symbol, amount), 'currencyPair': market['id'], }; if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); } const response = await this[method] (this.extend (request, params)); const result = { 'info': response, 'id': response['orderId'].toString (), }; const success = this.safeValue (response, 'success'); if (success) { result['status'] = 'open'; } return result; } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'orderId': id, 'currencyPair': market['id'], }; const response = await this.privatePostExchangeCancellimit (this.extend (request, params)); const message = this.safeString (response, 'message', this.json (response)); if ('success' in response) { if (!response['success']) { throw new InvalidOrder (message); } else if ('cancelled' in response) { if (response['cancelled']) { return { 'status': 'canceled', 'info': response, }; } else { throw new OrderNotFound (message); } } } throw new ExchangeError (this.id + ' cancelOrder() failed: ' + this.json (response)); } async withdraw (code, amount, address, tag = undefined, params = {}) { // Sometimes the response with be { key: null } for all keys. // An example is if you attempt to withdraw more than is allowed when withdrawal fees are considered. this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); let wallet = address; if (tag !== undefined) { wallet += '::' + tag; } const request = { 'amount': this.decimalToPrecision (amount, TRUNCATE, currency['precision'], DECIMAL_PLACES), 'currency': currency['id'], 'wallet': wallet, }; const response = await this.privatePostPaymentOutCoin (this.extend (request, params)); const id = this.safeInteger (response, 'id'); if (id === undefined) { throw new InsufficientFunds (this.id + ' insufficient funds to cover requested withdrawal amount post fees ' + this.json (response)); } return { 'info': response, 'id': id, }; } parseTransaction (transaction, currency = undefined) { // { // "id": "c853093d5aa06df1c92d79c2...", (tx on deposits, address on withdrawals) // "type": "DEPOSIT", // "date": 1553186482676, // "amount": 712.61266, // "fee": 0, // "fixedCurrency": "XVG", // "taxCurrency": "XVG", // "variableAmount": null, // "variableCurrency": null, // "external": "Coin", // "login": "USERNAME", // "externalKey": "....87diPBy......3hTtuwUT78Yi", (address on deposits, tx on withdrawals) // "documentId": 1110662453 // }, let txid = undefined; let address = undefined; const id = this.safeString (transaction, 'documentId'); const amount = this.safeFloat (transaction, 'amount'); const timestamp = this.safeInteger (transaction, 'date'); const type = this.safeStringLower (transaction, 'type'); const currencyId = this.safeString (transaction, 'fixedCurrency'); const feeCost = this.safeFloat (transaction, 'fee'); const code = this.safeCurrencyCode (currencyId, currency); if (type === 'withdrawal') { txid = this.safeString (transaction, 'externalKey'); address = this.safeString (transaction, 'id'); } else if (type === 'deposit') { address = this.safeString (transaction, 'externalKey'); txid = this.safeString (transaction, 'id'); } let status = undefined; if (type === 'deposit') { status = 'ok'; // Deposits is not registered until they are in account. Withdrawals are left as undefined, not entirely sure about theyre status. } return { 'info': transaction, 'id': id, 'currency': code, 'amount': amount, 'address': address, 'tag': undefined, 'status': status, 'type': type, 'updated': undefined, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': { 'currency': code, 'cost': feeCost, }, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const endtime = 2505600000; // 29 days - exchange has maximum 30 days. const now = this.milliseconds (); const request = { 'types': 'DEPOSIT', 'end': now, 'start': (since !== undefined) ? parseInt (since) : now - endtime, }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); } if (limit !== undefined) { request['limit'] = limit; // default is 100 } const response = await this.privateGetPaymentHistoryTransactions (this.extend (request, params)); return this.parseTransactions (response, currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const endtime = 2505600000; // 29 days - exchange has maximum 30 days. const now = this.milliseconds (); const request = { 'types': 'WITHDRAWAL', 'end': now, 'start': (since !== undefined) ? parseInt (since) : now - endtime, }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); } if (limit !== undefined) { request['limit'] = limit; // default is 100 } if (since !== undefined) { request['start'] = since; } const response = await this.privateGetPaymentHistoryTransactions (this.extend (request, params)); return this.parseTransactions (response, currency, since, limit); } async fetchDepositAddress (currency, params = {}) { const request = { 'currency': currency, }; const response = await this.privateGetPaymentGetAddress (this.extend (request, params)); let address = this.safeString (response, 'wallet'); let tag = undefined; if (address.indexOf (':') >= 0) { const parts = address.split (':'); address = parts[0]; tag = parts[2]; } this.checkAddress (address); return { 'currency': currency, 'address': address, 'tag': tag, 'info': response, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + path; const query = this.urlencode (this.keysort (params)); if (method === 'GET') { if (Object.keys (params).length) { url += '?' + query; } } if (api === 'private') { this.checkRequiredCredentials (); if (method === 'POST') { body = query; } const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha256'); headers = { 'Api-Key': this.apiKey, 'Sign': signature.toUpperCase (), 'Content-Type': 'application/x-www-form-urlencoded', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if (code >= 300) { const feedback = this.id + ' ' + body; const errorCode = this.safeString (response, 'errorCode'); this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); throw new ExchangeError (feedback); } // returns status code 200 even if success === false const success = this.safeValue (response, 'success', true); if (!success) { const feedback = this.id + ' ' + body; const message = this.safeString2 (response, 'message', 'exception'); if (message !== undefined) { this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); } throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],108:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class luno extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'luno', 'name': 'luno', 'countries': [ 'GB', 'SG', 'ZA' ], 'rateLimit': 1000, 'version': '1', 'has': { 'CORS': false, 'fetchTickers': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchMyTrades': true, 'fetchTradingFee': true, 'fetchTradingFees': true, }, 'urls': { 'referral': 'https://www.luno.com/invite/44893A', 'logo': 'https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg', 'api': 'https://api.mybitx.com/api', 'www': 'https://www.luno.com', 'doc': [ 'https://www.luno.com/en/api', 'https://npmjs.org/package/bitx', 'https://github.com/bausmeier/node-bitx', ], }, 'api': { 'public': { 'get': [ 'orderbook', 'orderbook_top', 'ticker', 'tickers', 'trades', ], }, 'private': { 'get': [ 'accounts/{id}/pending', 'accounts/{id}/transactions', 'balance', 'fee_info', 'funding_address', 'listorders', 'listtrades', 'orders/{id}', 'quotes/{id}', 'withdrawals', 'withdrawals/{id}', ], 'post': [ 'accounts', 'postorder', 'marketorder', 'stoporder', 'funding_address', 'withdrawals', 'send', 'quotes', 'oauth2/grant', ], 'put': [ 'quotes/{id}', ], 'delete': [ 'quotes/{id}', 'withdrawals/{id}', ], }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetTickers (params); const result = []; for (let i = 0; i < response['tickers'].length; i++) { const market = response['tickers'][i]; const id = market['pair']; const baseId = id.slice (0, 3); const quoteId = id.slice (3, 6); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': undefined, 'precision': this.precision, 'limits': this.limits, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetBalance (params); const wallets = this.safeValue (response, 'balance', []); const result = { 'info': response }; for (let i = 0; i < wallets.length; i++) { const wallet = wallets[i]; const currencyId = this.safeString (wallet, 'asset'); const code = this.safeCurrencyCode (currencyId); const reserved = this.safeFloat (wallet, 'reserved'); const unconfirmed = this.safeFloat (wallet, 'unconfirmed'); const balance = this.safeFloat (wallet, 'balance'); const account = this.account (); account['used'] = this.sum (reserved, unconfirmed); account['total'] = this.sum (balance, unconfirmed); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); let method = 'publicGetOrderbook'; if (limit !== undefined) { if (limit <= 100) { method += 'Top'; // get just the top of the orderbook when limit is low } } const request = { 'pair': this.marketId (symbol), }; const response = await this[method] (this.extend (request, params)); const timestamp = this.safeInteger (response, 'timestamp'); return this.parseOrderBook (response, timestamp, 'bids', 'asks', 'price', 'volume'); } parseOrder (order, market = undefined) { // // { // "base": "string", // "completed_timestamp": "string", // "counter": "string", // "creation_timestamp": "string", // "expiration_timestamp": "string", // "fee_base": "string", // "fee_counter": "string", // "limit_price": "string", // "limit_volume": "string", // "order_id": "string", // "pair": "string", // "state": "PENDING", // "type": "BID" // } // const timestamp = this.safeInteger (order, 'creation_timestamp'); const status = (order['state'] === 'PENDING') ? 'open' : 'closed'; const side = (order['type'] === 'ASK') ? 'sell' : 'buy'; const marketId = this.safeString (order, 'pair'); let symbol = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } if (market !== undefined) { symbol = market['symbol']; } const price = this.safeFloat (order, 'limit_price'); const amount = this.safeFloat (order, 'limit_volume'); const quoteFee = this.safeFloat (order, 'fee_counter'); const baseFee = this.safeFloat (order, 'fee_base'); const filled = this.safeFloat (order, 'base'); const cost = this.safeFloat (order, 'counter'); let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = Math.max (0, amount - filled); } } const fee = { 'currency': undefined }; if (quoteFee) { fee['cost'] = quoteFee; if (market !== undefined) { fee['currency'] = market['quote']; } } else { fee['cost'] = baseFee; if (market !== undefined) { fee['currency'] = market['base']; } } const id = this.safeString (order, 'order_id'); return { 'id': id, 'clientOrderId': undefined, 'datetime': this.iso8601 (timestamp), 'timestamp': timestamp, 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'filled': filled, 'cost': cost, 'remaining': remaining, 'trades': undefined, 'fee': fee, 'info': order, 'average': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privateGetOrdersId (this.extend (request, params)); return this.parseOrder (response); } async fetchOrdersByState (state = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (state !== undefined) { request['state'] = state; } if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } const response = await this.privateGetListorders (this.extend (request, params)); const orders = this.safeValue (response, 'orders', []); return this.parseOrders (orders, market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState (undefined, symbol, since, limit, params); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState ('PENDING', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState ('COMPLETE', symbol, since, limit, params); } parseTicker (ticker, market = undefined) { const timestamp = this.safeInteger (ticker, 'timestamp'); let symbol = undefined; if (market) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last_trade'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'rolling_24_hour_volume'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickers (params); const tickers = this.indexBy (response['tickers'], 'pair'); const ids = Object.keys (tickers); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = this.markets_by_id[id]; const symbol = market['symbol']; const ticker = tickers[id]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetTicker (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market) { // For public trade data (is_buy === True) indicates 'buy' side but for private trade data // is_buy indicates maker or taker. The value of "type" (ASK/BID) indicate sell/buy side. // Private trade data includes ID field which public trade data does not. const orderId = this.safeString (trade, 'order_id'); let takerOrMaker = undefined; let side = undefined; if (orderId !== undefined) { side = (trade['type'] === 'ASK') ? 'sell' : 'buy'; if (side === 'sell' && trade['is_buy']) { takerOrMaker = 'maker'; } else if (side === 'buy' && !trade['is_buy']) { takerOrMaker = 'maker'; } else { takerOrMaker = 'taker'; } } else { side = trade['is_buy'] ? 'buy' : 'sell'; } const feeBase = this.safeFloat (trade, 'fee_base'); const feeCounter = this.safeFloat (trade, 'fee_counter'); let feeCurrency = undefined; let feeCost = undefined; if (feeBase !== undefined) { if (feeBase !== 0.0) { feeCurrency = market['base']; feeCost = feeBase; } } else if (feeCounter !== undefined) { if (feeCounter !== 0.0) { feeCurrency = market['quote']; feeCost = feeCounter; } } const timestamp = this.safeInteger (trade, 'timestamp'); return { 'info': trade, 'id': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': market['symbol'], 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': this.safeFloat (trade, 'price'), 'amount': this.safeFloat (trade, 'volume'), // Does not include potential fee costs 'cost': this.safeFloat (trade, 'counter'), 'fee': { 'cost': feeCost, 'currency': feeCurrency, }, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (since !== undefined) { request['since'] = since; } const response = await this.publicGetTrades (this.extend (request, params)); const trades = this.safeValue (response, 'trades', []); return this.parseTrades (trades, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (since !== undefined) { request['since'] = since; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetListtrades (this.extend (request, params)); const trades = this.safeValue (response, 'trades', []); return this.parseTrades (trades, market, since, limit); } async fetchTradingFees (params = {}) { await this.loadMarkets (); const response = await this.privateGetFeeInfo (params); return { 'info': response, 'maker': this.safeFloat (response, 'maker_fee'), 'taker': this.safeFloat (response, 'taker_fee'), }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); let method = 'privatePost'; const request = { 'pair': this.marketId (symbol), }; if (type === 'market') { method += 'Marketorder'; request['type'] = side.toUpperCase (); if (side === 'buy') { request['counter_volume'] = amount; } else { request['base_volume'] = amount; } } else { method += 'Postorder'; request['volume'] = amount; request['price'] = price; request['type'] = (side === 'buy') ? 'BID' : 'ASK'; } const response = await this[method] (this.extend (request, params)); return { 'info': response, 'id': response['order_id'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': id, }; return await this.privatePostStoporder (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } if (api === 'private') { this.checkRequiredCredentials (); let auth = this.encode (this.apiKey + ':' + this.secret); auth = this.stringToBase64 (auth); headers = { 'Authorization': 'Basic ' + this.decode (auth) }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('error' in response) { throw new ExchangeError (this.id + ' ' + this.json (response)); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],109:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); // --------------------------------------------------------------------------- module.exports = class lykke extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'lykke', 'name': 'Lykke', 'countries': [ 'CH' ], 'version': 'v1', 'rateLimit': 200, 'has': { 'CORS': false, 'fetchOHLCV': false, 'fetchTrades': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchOrder': true, 'fetchOrders': true, }, 'requiredCredentials': { 'apiKey': true, 'secret': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/34487620-3139a7b0-efe6-11e7-90f5-e520cef74451.jpg', 'api': { 'mobile': 'https://public-api.lykke.com/api', 'public': 'https://hft-api.lykke.com/api', 'private': 'https://hft-api.lykke.com/api', }, 'test': { 'mobile': 'https://public-api.lykke.com/api', 'public': 'https://hft-service-dev.lykkex.net/api', 'private': 'https://hft-service-dev.lykkex.net/api', }, 'www': 'https://www.lykke.com', 'doc': [ 'https://hft-api.lykke.com/swagger/ui/', 'https://www.lykke.com/lykke_api', ], 'fees': 'https://www.lykke.com/trading-conditions', }, 'api': { 'mobile': { 'get': [ 'Market/{market}', 'Trades/{AssetPairId}', ], }, 'public': { 'get': [ 'AssetPairs', 'AssetPairs/{id}', 'IsAlive', 'OrderBooks', 'OrderBooks/{AssetPairId}', ], }, 'private': { 'get': [ 'Orders', 'Orders/{id}', 'Wallets', ], 'post': [ 'Orders/limit', 'Orders/market', 'Orders/{id}/Cancel', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.0, // as of 7 Feb 2018, see https://github.com/ccxt/ccxt/issues/1863 'taker': 0.0, // https://www.lykke.com/cp/wallet-fees-and-limits }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BTC': 0.001, }, 'deposit': { 'BTC': 0, }, }, }, 'commonCurrencies': { 'XPD': 'Lykke XPD', }, }); } parseTrade (trade, market) { // // public fetchTrades // // { // "id": "d5983ab8-e9ec-48c9-bdd0-1b18f8e80a71", // "assetPairId": "BTCUSD", // "dateTime": "2019-05-15T06:52:02.147Z", // "volume": 0.00019681, // "index": 0, // "price": 8023.333, // "action": "Buy" // } // let symbol = undefined; if (market === undefined) { const marketId = this.safeString (trade, 'AssetPairId'); market = this.safeValue (this.markets_by_id, marketId); } if (market) { symbol = market['symbol']; } const id = this.safeString (trade, 'id'); const timestamp = this.parse8601 (this.safeString (trade, 'dateTime')); const side = this.safeStringLower (trade, 'action'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'volume'); const cost = price * amount; return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'order': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 100; } const request = { 'AssetPairId': market['id'], 'skip': 0, 'take': limit, }; const response = await this.mobileGetTradesAssetPairId (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetWallets (params); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'AssetId'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balance, 'Balance'); account['used'] = this.safeFloat (balance, 'Reserved'); result[code] = account; } return this.parseBalance (result); } async cancelOrder (id, symbol = undefined, params = {}) { return await this.privatePostOrdersIdCancel ({ 'id': id }); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const query = { 'AssetPairId': market['id'], 'OrderAction': this.capitalize (side), 'Volume': amount, }; if (type === 'market') { query['Asset'] = (side === 'buy') ? market['base'] : market['quote']; } else if (type === 'limit') { query['Price'] = price; } const method = 'privatePostOrders' + this.capitalize (type); const result = await this[method] (this.extend (query, params)); return { 'id': undefined, 'info': result, }; } async fetchMarkets (params = {}) { const markets = await this.publicGetAssetPairs (); // // [ { Id: "AEBTC", // Name: "AE/BTC", // Accuracy: 6, // InvertedAccuracy: 8, // BaseAssetId: "6f75280b-a005-4016-a3d8-03dc644e8912", // QuotingAssetId: "BTC", // MinVolume: 0.4, // MinInvertedVolume: 0.0001 }, // { Id: "AEETH", // Name: "AE/ETH", // Accuracy: 6, // InvertedAccuracy: 8, // BaseAssetId: "6f75280b-a005-4016-a3d8-03dc644e8912", // QuotingAssetId: "ETH", // MinVolume: 0.4, // MinInvertedVolume: 0.001 } ] // const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'Id'); const name = this.safeString (market, 'Name'); const [ baseId, quoteId ] = name.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'Accuracy'), 'price': this.safeInteger (market, 'InvertedAccuracy'), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'active': true, 'info': market, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'baseId': undefined, 'quoteId': undefined, }); } return result; } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market) { symbol = market['symbol']; } const close = this.safeFloat (ticker, 'lastPrice'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': close, 'last': close, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeFloat (ticker, 'volume24H'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const ticker = await this.mobileGetMarketMarket (this.extend (request, params)); return this.parseTicker (ticker, market); } parseOrderStatus (status) { const statuses = { 'Open': 'open', 'Pending': 'open', 'InOrderBook': 'open', 'Processing': 'open', 'Matched': 'closed', 'Cancelled': 'canceled', 'Rejected': 'rejected', 'Replaced': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "Id": "string", // "Status": "Unknown", // "AssetPairId": "string", // "Volume": 0, // "Price": 0, // "RemainingVolume": 0, // "LastMatchTime": "2020-03-26T20:58:50.710Z", // "CreatedAt": "2020-03-26T20:58:50.710Z", // "Type": "Unknown", // "LowerLimitPrice": 0, // "LowerPrice": 0, // "UpperLimitPrice": 0, // "UpperPrice": 0 // } // const status = this.parseOrderStatus (this.safeString (order, 'Status')); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'AssetPairId'); market = this.safeValue (this.markets_by_id, marketId); } if (market) { symbol = market['symbol']; } const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'LastMatchTime')); let timestamp = undefined; if (('Registered' in order) && (order['Registered'])) { timestamp = this.parse8601 (order['Registered']); } else if (('CreatedAt' in order) && (order['CreatedAt'])) { timestamp = this.parse8601 (order['CreatedAt']); } const price = this.safeFloat (order, 'Price'); const amount = this.safeFloat (order, 'Volume'); const remaining = this.safeFloat (order, 'RemainingVolume'); const filled = amount - remaining; const cost = filled * price; const id = this.safeString (order, 'Id'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': undefined, 'side': undefined, 'price': price, 'cost': cost, 'average': undefined, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const response = await this.privateGetOrdersId (this.extend (request, params)); return this.parseOrder (response); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.privateGetOrders (params); return this.parseOrders (response, undefined, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'status': 'InOrderBook', }; const response = await this.privateGetOrders (this.extend (request, params)); return this.parseOrders (response, undefined, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'status': 'Matched', }; const response = await this.privateGetOrders (this.extend (request, params)); return this.parseOrders (response, undefined, since, limit); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetOrderBooksAssetPairId (this.extend ({ 'AssetPairId': this.marketId (symbol), }, params)); const orderbook = { 'timestamp': undefined, 'bids': [], 'asks': [], }; let timestamp = undefined; for (let i = 0; i < response.length; i++) { const side = response[i]; if (side['IsBuy']) { orderbook['bids'] = this.arrayConcat (orderbook['bids'], side['Prices']); } else { orderbook['asks'] = this.arrayConcat (orderbook['asks'], side['Prices']); } const sideTimestamp = this.parse8601 (side['Timestamp']); timestamp = (timestamp === undefined) ? sideTimestamp : Math.max (timestamp, sideTimestamp); } return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'Price', 'Volume'); } parseBidAsk (bidask, priceKey = 0, amountKey = 1) { const price = this.safeFloat (bidask, priceKey); let amount = this.safeFloat (bidask, amountKey); if (amount < 0) { amount = -amount; } return [ price, amount ]; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'mobile') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'private') { if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } this.checkRequiredCredentials (); headers = { 'api-key': this.apiKey, 'Accept': 'application/json', 'Content-Type': 'application/json', }; if (method === 'POST') { if (Object.keys (params).length) { body = this.json (params); } } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7}],110:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, InvalidOrder } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class mercado extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'mercado', 'name': 'Mercado Bitcoin', 'countries': [ 'BR' ], // Brazil 'rateLimit': 1000, 'version': 'v3', 'has': { 'CORS': true, 'createMarketOrder': true, 'fetchOrder': true, 'withdraw': true, 'fetchOHLCV': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchTicker': true, 'fetchTickers': false, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '6h': '6h', '12h': '12h', '1d': '1d', '3d': '3d', '1w': '1w', '2w': '2w', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27837060-e7c58714-60ea-11e7-9192-f05e86adb83f.jpg', 'api': { 'public': 'https://www.mercadobitcoin.net/api', 'private': 'https://www.mercadobitcoin.net/tapi', 'v4Public': 'https://www.mercadobitcoin.com.br/v4', }, 'www': 'https://www.mercadobitcoin.com.br', 'doc': [ 'https://www.mercadobitcoin.com.br/api-doc', 'https://www.mercadobitcoin.com.br/trade-api', ], }, 'api': { 'public': { 'get': [ '{coin}/orderbook/', // last slash critical '{coin}/ticker/', '{coin}/trades/', '{coin}/trades/{from}/', '{coin}/trades/{from}/{to}', '{coin}/day-summary/{year}/{month}/{day}/', ], }, 'private': { 'post': [ 'cancel_order', 'get_account_info', 'get_order', 'get_withdrawal', 'list_system_messages', 'list_orders', 'list_orderbook', 'place_buy_order', 'place_sell_order', 'place_market_buy_order', 'place_market_sell_order', 'withdraw_coin', ], }, 'v4Public': { 'get': [ '{coin}/candle/', ], }, }, 'markets': { 'BTC/BRL': { 'id': 'BRLBTC', 'symbol': 'BTC/BRL', 'base': 'BTC', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Bitcoin' }, 'LTC/BRL': { 'id': 'BRLLTC', 'symbol': 'LTC/BRL', 'base': 'LTC', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Litecoin' }, 'BCH/BRL': { 'id': 'BRLBCH', 'symbol': 'BCH/BRL', 'base': 'BCH', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'BCash' }, 'XRP/BRL': { 'id': 'BRLXRP', 'symbol': 'XRP/BRL', 'base': 'XRP', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Ripple' }, 'ETH/BRL': { 'id': 'BRLETH', 'symbol': 'ETH/BRL', 'base': 'ETH', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Ethereum' }, }, 'fees': { 'trading': { 'maker': 0.3 / 100, 'taker': 0.7 / 100, }, }, }); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin': market['base'], }; const response = await this.publicGetCoinOrderbook (this.extend (request, params)); return this.parseOrderBook (response); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin': market['base'], }; const response = await this.publicGetCoinTicker (this.extend (request, params)); const ticker = this.safeValue (response, 'ticker', {}); const timestamp = this.safeTimestamp (ticker, 'date'); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': undefined, 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (trade, 'tid'); const type = undefined; const side = this.safeString (trade, 'type'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': type, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let method = 'publicGetCoinTrades'; const request = { 'coin': market['base'], }; if (since !== undefined) { method += 'From'; request['from'] = parseInt (since / 1000); } const to = this.safeInteger (params, 'to'); if (to !== undefined) { method += 'To'; } const response = await this[method] (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGetAccountInfo (params); const data = this.safeValue (response, 'response_data', {}); const balances = this.safeValue (data, 'balance', {}); const result = { 'info': response }; const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); if (currencyId in balances) { const balance = this.safeValue (balances, currencyId, {}); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['total'] = this.safeFloat (balance, 'total'); result[code] = account; } } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'coin_pair': this.marketId (symbol), }; let method = this.capitalize (side) + 'Order'; if (type === 'limit') { method = 'privatePostPlace' + method; request['limit_price'] = this.priceToPrecision (symbol, price); request['quantity'] = this.amountToPrecision (symbol, amount); } else { method = 'privatePostPlaceMarket' + method; if (side === 'buy') { if (price === undefined) { throw new InvalidOrder (this.id + ' createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount'); } request['cost'] = this.priceToPrecision (symbol, amount * price); } else { request['quantity'] = this.amountToPrecision (symbol, amount); } } const response = await this[method] (this.extend (request, params)); // TODO: replace this with a call to parseOrder for unification return { 'info': response, 'id': response['response_data']['order']['order_id'].toString (), }; } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder () requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin_pair': market['id'], 'order_id': id, }; const response = await this.privatePostCancelOrder (this.extend (request, params)); // // { // response_data: { // order: { // order_id: 2176769, // coin_pair: 'BRLBCH', // order_type: 2, // status: 3, // has_fills: false, // quantity: '0.10000000', // limit_price: '1996.15999', // executed_quantity: '0.00000000', // executed_price_avg: '0.00000', // fee: '0.00000000', // created_timestamp: '1536956488', // updated_timestamp: '1536956499', // operations: [] // } // }, // status_code: 100, // server_unix_timestamp: '1536956499' // } // const responseData = this.safeValue (response, 'response_data', {}); const order = this.safeValue (responseData, 'order', {}); return this.parseOrder (order, market); } parseOrderStatus (status) { const statuses = { '2': 'open', '3': 'canceled', '4': 'closed', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "order_id": 4, // "coin_pair": "BRLBTC", // "order_type": 1, // "status": 2, // "has_fills": true, // "quantity": "2.00000000", // "limit_price": "900.00000", // "executed_quantity": "1.00000000", // "executed_price_avg": "900.00000", // "fee": "0.00300000", // "created_timestamp": "1453838494", // "updated_timestamp": "1453838494", // "operations": [ // { // "operation_id": 1, // "quantity": "1.00000000", // "price": "900.00000", // "fee_rate": "0.30", // "executed_timestamp": "1453838494", // }, // ], // } // const id = this.safeString (order, 'order_id'); let side = undefined; if ('order_type' in order) { side = (order['order_type'] === 1) ? 'buy' : 'sell'; } const status = this.parseOrderStatus (this.safeString (order, 'status')); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'coin_pair'); market = this.safeValue (this.markets_by_id, marketId); } if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.safeTimestamp (order, 'created_timestamp'); const fee = { 'cost': this.safeFloat (order, 'fee'), 'currency': market['quote'], }; const price = this.safeFloat (order, 'limit_price'); // price = this.safeFloat (order, 'executed_price_avg', price); const average = this.safeFloat (order, 'executed_price_avg'); const amount = this.safeFloat (order, 'quantity'); const filled = this.safeFloat (order, 'executed_quantity'); const remaining = amount - filled; const cost = filled * average; const lastTradeTimestamp = this.safeTimestamp (order, 'updated_timestamp'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, // todo parse trades (operations) }; } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder () requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin_pair': market['id'], 'order_id': parseInt (id), }; const response = await this.privatePostGetOrder (this.extend (request, params)); const responseData = this.safeValue (response, 'response_data', {}); const order = this.safeValue (responseData, 'order'); return this.parseOrder (order, market); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'coin': currency['id'], 'quantity': amount.toFixed (10), 'address': address, }; if (code === 'BRL') { const account_ref = ('account_ref' in params); if (!account_ref) { throw new ArgumentsRequired (this.id + ' requires account_ref parameter to withdraw ' + code); } } else if (code !== 'LTC') { const tx_fee = ('tx_fee' in params); if (!tx_fee) { throw new ArgumentsRequired (this.id + ' requires tx_fee parameter to withdraw ' + code); } if (code === 'XRP') { if (tag === undefined) { if (!('destination_tag' in params)) { throw new ArgumentsRequired (this.id + ' requires a tag argument or destination_tag parameter to withdraw ' + code); } } else { request['destination_tag'] = tag; } } } const response = await this.privatePostWithdrawCoin (this.extend (request, params)); return { 'info': response, 'id': response['response_data']['withdrawal']['id'], }; } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ this.safeTimestamp (ohlcv, 'timestamp'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'precision': this.timeframes[timeframe], 'coin': market['id'].toLowerCase (), }; if (limit !== undefined && since !== undefined) { request['from'] = parseInt (since / 1000); request['to'] = this.sum (request['from'], limit * this.parseTimeframe (timeframe)); } else if (since !== undefined) { request['from'] = parseInt (since / 1000); request['to'] = this.sum (this.seconds (), 1); } else if (limit !== undefined) { request['to'] = this.seconds (); request['from'] = request['to'] - (limit * this.parseTimeframe (timeframe)); } const response = await this.v4PublicGetCoinCandle (this.extend (request, params)); const candles = this.safeValue (response, 'candles', []); return this.parseOHLCVs (candles, market, timeframe, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders () requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'coin_pair': market['id'], }; const response = await this.privatePostListOrders (this.extend (request, params)); const responseData = this.safeValue (response, 'response_data', {}); const orders = this.safeValue (responseData, 'orders', []); return this.parseOrders (orders, market, since, limit); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api] + '/'; const query = this.omit (params, this.extractParams (path)); if (api === 'public' || (api === 'v4Public')) { url += this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); url += this.version + '/'; const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'tapi_method': path, 'tapi_nonce': nonce, }, params)); const auth = '/tapi/' + this.version + '/' + '?' + body; headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'TAPI-ID': this.apiKey, 'TAPI-MAC': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('error_message' in response) { throw new ExchangeError (this.id + ' ' + this.json (response)); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],111:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class mixcoins extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'mixcoins', 'name': 'MixCoins', 'countries': [ 'GB', 'HK' ], 'rateLimit': 1500, 'version': 'v1', 'userAgent': this.userAgents['chrome'], 'has': { 'CORS': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/30237212-ed29303c-9535-11e7-8af8-fcd381cfa20c.jpg', 'api': 'https://mixcoins.com/api', 'www': 'https://mixcoins.com', 'doc': 'https://mixcoins.com/help/api/', }, 'api': { 'public': { 'get': [ 'ticker/', 'trades/', 'depth/', ], }, 'private': { 'post': [ 'cancel', 'info', 'orders', 'order', 'transactions', 'trade', ], }, }, 'markets': { 'BTC/USDT': { 'id': 'btc_usdt', 'symbol': 'BTC/USDT', 'base': 'BTC', 'quote': 'USDT', 'baseId': 'btc', 'quoteId': 'usdt', 'maker': 0.0015, 'taker': 0.0025 }, 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'maker': 0.001, 'taker': 0.0015 }, 'BCH/BTC': { 'id': 'bch_btc', 'symbol': 'BCH/BTC', 'base': 'BCH', 'quote': 'BTC', 'baseId': 'bch', 'quoteId': 'btc', 'maker': 0.001, 'taker': 0.0015 }, 'LSK/BTC': { 'id': 'lsk_btc', 'symbol': 'LSK/BTC', 'base': 'LSK', 'quote': 'BTC', 'baseId': 'lsk', 'quoteId': 'btc', 'maker': 0.0015, 'taker': 0.0025 }, 'BCH/USDT': { 'id': 'bch_usdt', 'symbol': 'BCH/USDT', 'base': 'BCH', 'quote': 'USDT', 'baseId': 'bch', 'quoteId': 'usdt', 'maker': 0.001, 'taker': 0.0015 }, 'ETH/USDT': { 'id': 'eth_usdt', 'symbol': 'ETH/USDT', 'base': 'ETH', 'quote': 'USDT', 'baseId': 'eth', 'quoteId': 'usdt', 'maker': 0.001, 'taker': 0.0015 }, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostInfo (params); const balances = this.safeValue (response['result'], 'wallet'); const result = { 'info': response }; const currencyIds = Object.keys (balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const balance = this.safeValue (balances, currencyId, {}); const account = this.account (); account['free'] = this.safeFloat (balance, 'avail'); account['used'] = this.safeFloat (balance, 'lock'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), }; const response = await this.publicGetDepth (this.extend (request, params)); return this.parseOrderBook (response['result']); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), }; const response = await this.publicGetTicker (this.extend (request, params)); const ticker = this.safeValue (response, 'result'); const timestamp = this.milliseconds (); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': undefined, 'info': ticker, }; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (trade, 'id'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response['result'], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), 'op': side, 'amount': amount, }; if (type === 'market') { request['order_type'] = 1; request['price'] = price; } else { request['order_type'] = 0; } const response = await this.privatePostTrade (this.extend (request, params)); return { 'info': response, 'id': response['result']['id'].toString (), }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; return await this.privatePostCancel (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + path; if (api === 'public') { if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'nonce': nonce, }, params)); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': this.hmac (this.encode (body), this.secret, 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('status' in response) { // // todo add a unified standard handleErrors with this.exceptions in describe() // // {"status":503,"message":"Maintenancing, try again later","result":null} // if (response['status'] === 200) { return response; } } throw new ExchangeError (this.id + ' ' + this.json (response)); } }; },{"./base/Exchange":7,"./base/errors":9}],112:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, AuthenticationError, ArgumentsRequired, BadRequest, InvalidOrder, InsufficientFunds, OrderNotFound, PermissionDenied } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class oceanex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'oceanex', 'name': 'OceanEx', 'countries': [ 'US' ], 'version': 'v1', 'rateLimit': 3000, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/58385970-794e2d80-8001-11e9-889c-0567cd79b78e.jpg', 'api': 'https://api.oceanex.pro', 'www': 'https://www.oceanex.pro.com', 'doc': 'https://api.oceanex.pro/doc/v1', 'referral': 'https://oceanex.pro/signup?referral=VE24QX', }, 'has': { 'fetchMarkets': true, 'fetchCurrencies': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchOrderBook': true, 'fetchOrderBooks': true, 'fetchTrades': true, 'fetchTradingLimits': false, 'fetchTradingFees': false, 'fetchAllTradingFees': true, 'fetchFundingFees': false, 'fetchTime': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchBalance': true, 'createMarketOrder': true, 'createOrder': true, 'cancelOrder': true, 'cancelOrders': true, 'cancelAllOrders': true, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '4h': '4h', '12h': '12h', '1d': '1d', '1w': '1w', }, 'api': { 'public': { 'get': [ 'markets', 'tickers/{pair}', 'tickers_multi', 'order_book', 'order_book/multi', 'fees/trading', 'trades', 'timestamp', ], }, 'private': { 'get': [ 'key', 'members/me', 'orders', 'orders/filter', ], 'post': [ 'orders', 'orders/multi', 'order/delete', 'order/delete/multi', 'orders/clear', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.1 / 100, 'taker': 0.1 / 100, }, }, 'commonCurrencies': { 'PLA': 'Plair', }, 'exceptions': { 'codes': { '-1': BadRequest, '-2': BadRequest, '1001': BadRequest, '1004': ArgumentsRequired, '1006': AuthenticationError, '1008': AuthenticationError, '1010': AuthenticationError, '1011': PermissionDenied, '2001': AuthenticationError, '2002': InvalidOrder, '2004': OrderNotFound, '9003': PermissionDenied, }, 'exact': { 'market does not have a valid value': BadRequest, 'side does not have a valid value': BadRequest, 'Account::AccountError: Cannot lock funds': InsufficientFunds, 'The account does not exist': AuthenticationError, }, }, }); } async fetchMarkets (params = {}) { const request = { 'show_details': true }; const response = await this.publicGetMarkets (this.extend (request, params)); const result = []; const markets = this.safeValue (response, 'data'); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeValue (market, 'id'); const name = this.safeValue (market, 'name'); let [ baseId, quoteId ] = name.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); baseId = baseId.toLowerCase (); quoteId = quoteId.toLowerCase (); const symbol = base + '/' + quote; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'info': market, 'precision': { 'amount': this.safeValue (market, 'amount_precision'), 'price': this.safeValue (market, 'price_precision'), 'base': this.safeValue (market, 'ask_precision'), 'quote': this.safeValue (market, 'bid_precision'), }, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeValue (market, 'minimum_trading_amount'), 'max': undefined, }, }, }); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetTickersPair (this.extend (request, params)); // // { // "code":0, // "message":"Operation successful", // "data": { // "at":1559431729, // "ticker": { // "buy":"0.0065", // "sell":"0.00677", // "low":"0.00677", // "high":"0.00677", // "last":"0.00677", // "vol":"2000.0" // } // } // } // const data = this.safeValue (response, 'data', {}); return this.parseTicker (data, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); if (symbols === undefined) { symbols = this.symbols; } const marketIds = this.marketIds (symbols); const request = { 'markets': marketIds }; const response = await this.publicGetTickersMulti (this.extend (request, params)); // // { // "code":0, // "message":"Operation successful", // "data": { // "at":1559431729, // "ticker": { // "buy":"0.0065", // "sell":"0.00677", // "low":"0.00677", // "high":"0.00677", // "last":"0.00677", // "vol":"2000.0" // } // } // } // const data = this.safeValue (response, 'data'); const result = {}; for (let i = 0; i < data.length; i++) { const ticker = data[i]; const marketId = this.safeString (ticker, 'market'); const market = this.markets_by_id[marketId]; const symbol = market['symbol']; result[symbol] = this.parseTicker (ticker, market); } return result; } parseTicker (data, market = undefined) { // // { // "at":1559431729, // "ticker": { // "buy":"0.0065", // "sell":"0.00677", // "low":"0.00677", // "high":"0.00677", // "last":"0.00677", // "vol":"2000.0" // } // } // const ticker = this.safeValue (data, 'ticker', {}); const timestamp = this.safeTimestamp (data, 'at'); return { 'symbol': market['symbol'], 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': this.safeFloat (ticker, 'last'), 'last': this.safeFloat (ticker, 'last'), 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrderBook (this.extend (request, params)); // // { // "code":0, // "message":"Operation successful", // "data": { // "timestamp":1559433057, // "asks": [ // ["100.0","20.0"], // ["4.74","2000.0"], // ["1.74","4000.0"], // ], // "bids":[ // ["0.0065","5482873.4"], // ["0.00649","4781956.2"], // ["0.00648","2876006.8"], // ], // } // } // const orderbook = this.safeValue (response, 'data', {}); const timestamp = this.safeTimestamp (orderbook, 'timestamp'); return this.parseOrderBook (orderbook, timestamp); } async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { await this.loadMarkets (); if (symbols === undefined) { symbols = this.symbols; } const marketIds = this.marketIds (symbols); const request = { 'markets': marketIds, }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrderBookMulti (this.extend (request, params)); // // { // "code":0, // "message":"Operation successful", // "data": [ // { // "timestamp":1559433057, // "market": "bagvet", // "asks": [ // ["100.0","20.0"], // ["4.74","2000.0"], // ["1.74","4000.0"], // ], // "bids":[ // ["0.0065","5482873.4"], // ["0.00649","4781956.2"], // ["0.00648","2876006.8"], // ], // }, // ..., // ], // } // const data = this.safeValue (response, 'data', []); const result = {}; for (let i = 0; i < data.length; i++) { const orderbook = data[i]; const marketId = this.safeString (orderbook, 'market'); const market = this.markets_by_id[marketId]; const symbol = market['symbol']; const timestamp = this.safeTimestamp (orderbook, 'timestamp'); result[symbol] = this.parseOrderBook (orderbook, timestamp); } return result; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetTrades (this.extend (request, params)); const data = this.safeValue (response, 'data'); return this.parseTrades (data, market, since, limit); } parseTrade (trade, market = undefined) { let side = this.safeValue (trade, 'side'); if (side === 'bid') { side = 'buy'; } else if (side === 'ask') { side = 'sell'; } let symbol = undefined; const marketId = this.safeValue (trade, 'market'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = marketId; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } let timestamp = this.safeTimestamp (trade, 'created_on'); if (timestamp === undefined) { timestamp = this.parse8601 (this.safeString (trade, 'created_at')); } return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': this.safeString (trade, 'id'), 'order': undefined, 'type': 'limit', 'takerOrMaker': undefined, 'side': side, 'price': this.safeFloat (trade, 'price'), 'amount': this.safeFloat (trade, 'volume'), 'cost': undefined, 'fee': undefined, }; } async fetchTime (params = {}) { const response = await this.publicGetTimestamp (params); // // {"code":0,"message":"Operation successful","data":1559433420} // return this.safeTimestamp (response, 'data'); } async fetchAllTradingFees (params = {}) { const response = await this.publicGetFeesTrading (params); const data = this.safeValue (response, 'data'); const result = {}; for (let i = 0; i < data.length; i++) { const group = data[i]; const maker = this.safeValue (group, 'ask_fee', {}); const taker = this.safeValue (group, 'bid_fee', {}); const marketId = this.safeString (group, 'market'); let symbol = marketId; if (marketId in this.markets_by_id) { symbol = this.markets_by_id[marketId]['symbol']; } result[symbol] = { 'info': group, 'symbol': symbol, 'maker': this.safeFloat (maker, 'value'), 'taker': this.safeFloat (taker, 'value'), }; } return result; } async fetchKey (params = {}) { const response = await this.privateGetKey (params); return this.safeValue (response, 'data'); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetMembersMe (params); const data = this.safeValue (response, 'data'); const balances = this.safeValue (data, 'accounts'); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeValue (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'locked'); result[code] = account; } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'side': side, 'ord_type': type, 'volume': this.amountToPrecision (symbol, amount), }; if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.privatePostOrders (this.extend (request, params)); const data = this.safeValue (response, 'data'); return this.parseOrder (data, market); } async fetchOrder (id, symbol = undefined, params = {}) { let ids = id; if (!Array.isArray (id)) { ids = [ id ]; } await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = { 'ids': ids }; const response = await this.privateGetOrders (this.extend (request, params)); const data = this.safeValue (response, 'data'); const dataLength = data.length; if (data === undefined) { throw new OrderNotFound (this.id + ' could not found matching order'); } if (Array.isArray (id)) { return this.parseOrders (data, market); } if (dataLength === 0) { throw new OrderNotFound (this.id + ' could not found matching order'); } return this.parseOrder (data[0], market); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'states': [ 'wait' ], }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'states': [ 'done', 'cancel' ], }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders requires a `symbol` argument'); } await this.loadMarkets (); const market = this.market (symbol); const states = this.safeValue (params, 'states', [ 'wait', 'done', 'cancel' ]); const query = this.omit (params, 'states'); const request = { 'market': market['id'], 'states': states, 'need_price': 'True', }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetOrdersFilter (this.extend (request, query)); const data = this.safeValue (response, 'data', []); let result = []; for (let i = 0; i < data.length; i++) { const orders = this.safeValue (data[i], 'orders', []); const status = this.parseOrderStatus (this.safeValue (data[i], 'state')); const parsedOrders = this.parseOrders (orders, market, since, limit, { 'status': status }); result = this.arrayConcat (result, parsedOrders); } return result; } parseOrder (order, market = undefined) { // // { // "created_at": "2019-01-18T00:38:18Z", // "trades_count": 0, // "remaining_volume": "0.2", // "price": "1001.0", // "created_on": "1547771898", // "side": "buy", // "volume": "0.2", // "state": "wait", // "ord_type": "limit", // "avg_price": "0.0", // "executed_volume": "0.0", // "id": 473797, // "market": "veteth" // } // const status = this.parseOrderStatus (this.safeValue (order, 'state')); const marketId = this.safeValue2 (order, 'market', 'market_id'); let symbol = undefined; if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = marketId; } } if (symbol === undefined) { if (market !== undefined) { symbol = market['symbol']; } } let timestamp = this.safeTimestamp (order, 'created_on'); if (timestamp === undefined) { timestamp = this.parse8601 (this.safeString (order, 'created_at')); } return { 'info': order, 'id': this.safeString (order, 'id'), 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': this.safeValue (order, 'ord_type'), 'side': this.safeValue (order, 'side'), 'price': this.safeFloat (order, 'price'), 'average': this.safeFloat (order, 'avg_price'), 'amount': this.safeFloat (order, 'volume'), 'remaining': this.safeFloat (order, 'remaining_volume'), 'filled': this.safeFloat (order, 'executed_volume'), 'status': status, 'cost': undefined, 'trades': undefined, 'fee': undefined, }; } parseOrderStatus (status) { const statuses = { 'wait': 'open', 'done': 'closed', 'cancel': 'canceled', }; return this.safeString (statuses, status, status); } async createOrders (symbol, orders, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'orders': orders, }; // orders: [{"side":"buy", "volume":.2, "price":1001}, {"side":"sell", "volume":0.2, "price":1002}] const response = await this.privatePostOrdersMulti (this.extend (request, params)); const data = response['data']; return this.parseOrders (data); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const response = await this.privatePostOrderDelete (this.extend ({ 'id': id }, params)); const data = this.safeValue (response, 'data'); return this.parseOrder (data); } async cancelOrders (ids, symbol = undefined, params = {}) { await this.loadMarkets (); const response = await this.privatePostOrderDeleteMulti (this.extend ({ 'ids': ids }, params)); const data = this.safeValue (response, 'data'); return this.parseOrders (data); } async cancelAllOrders (symbol = undefined, params = {}) { await this.loadMarkets (); const response = await this.privatePostOrdersClear (params); const data = this.safeValue (response, 'data'); return this.parseOrders (data); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (path === 'tickers_multi' || path === 'order_book/multi') { let request = '?'; const markets = this.safeValue (params, 'markets'); for (let i = 0; i < markets.length; i++) { request += 'markets[]=' + markets[i] + '&'; } const limit = this.safeValue (params, 'limit'); if (limit !== undefined) { request += 'limit=' + limit; } url += request; } else if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'private') { this.checkRequiredCredentials (); const request = { 'uid': this.apiKey, 'data': query, }; // to set the private key: // const fs = require ('fs') // exchange.secret = fs.readFileSync ('oceanex.pem', 'utf8') const jwt_token = this.jwt (request, this.encode (this.secret), 'RS256'); url += '?user_jwt=' + jwt_token; } headers = { 'Content-Type': 'application/json' }; return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { // // {"code":1011,"message":"This IP '5.228.233.138' is not allowed","data":{}} // if (response === undefined) { return; } const errorCode = this.safeString (response, 'code'); const message = this.safeString (response, 'message'); if ((errorCode !== undefined) && (errorCode !== '0')) { const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['codes'], errorCode, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); throw new ExchangeError (response); } } }; },{"./base/Exchange":7,"./base/errors":9}],113:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const okex = require ('./okex.js'); // --------------------------------------------------------------------------- module.exports = class okcoin extends okex { describe () { return this.deepExtend (super.describe (), { 'id': 'okcoin', 'name': 'OKCoin', 'countries': [ 'CN', 'US' ], 'hostname': 'okcoin.com', 'pro': true, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766791-89ffb502-5ee5-11e7-8a5b-c5950b68ac65.jpg', 'www': 'https://www.okcoin.com', 'doc': 'https://www.okcoin.com/docs/en/', 'fees': 'https://www.okcoin.com/coin-fees', 'referral': 'https://www.okcoin.com/account/register?flag=activity&channelId=600001513', }, 'fees': { 'trading': { 'taker': 0.002, 'maker': 0.001, }, }, 'options': { 'fetchMarkets': [ 'spot' ], }, }); } }; },{"./okex.js":114}],114:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ExchangeNotAvailable, OnMaintenance, ArgumentsRequired, BadRequest, AccountSuspended, InvalidAddress, PermissionDenied, DDoSProtection, InsufficientFunds, InvalidNonce, CancelPending, InvalidOrder, OrderNotFound, AuthenticationError, RequestTimeout, NotSupported, BadSymbol } = require ('./base/errors'); const { TICK_SIZE, TRUNCATE } = require ('./base/functions/number'); // --------------------------------------------------------------------------- module.exports = class okex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'okex', 'name': 'OKEX', 'countries': [ 'CN', 'US' ], 'version': 'v3', 'rateLimit': 1000, // up to 3000 requests per 5 minutes ≈ 600 requests per minute ≈ 10 requests per second ≈ 100 ms 'pro': true, 'has': { 'CORS': false, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrders': false, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchCurrencies': false, // see below 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTime': true, 'fetchTransactions': false, 'fetchMyTrades': true, 'fetchDepositAddress': true, 'fetchOrderTrades': true, 'fetchTickers': true, 'fetchLedger': true, 'withdraw': true, 'futures': true, }, 'timeframes': { '1m': '60', '3m': '180', '5m': '300', '15m': '900', '30m': '1800', '1h': '3600', '2h': '7200', '4h': '14400', '6h': '21600', '12h': '43200', '1d': '86400', '1w': '604800', }, 'hostname': 'okex.com', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/32552768-0d6dd3c6-c4a6-11e7-90f8-c043b64756a7.jpg', 'api': { 'rest': 'https://www.{hostname}', }, 'www': 'https://www.okex.com', 'doc': 'https://www.okex.com/docs/en/', 'fees': 'https://www.okex.com/pages/products/fees.html', 'referral': 'https://www.okex.com/join/1888677', 'test': { 'rest': 'https://testnet.okex.com', }, }, 'api': { 'general': { 'get': [ 'time', ], }, 'account': { 'get': [ 'wallet', 'sub-account', 'asset-valuation', 'wallet/{currency}', 'withdrawal/history', 'withdrawal/history/{currency}', 'ledger', 'deposit/address', 'deposit/history', 'deposit/history/{currency}', 'currencies', 'withdrawal/fee', ], 'post': [ 'transfer', 'withdrawal', ], }, 'spot': { 'get': [ 'accounts', 'accounts/{currency}', 'accounts/{currency}/ledger', 'orders', 'orders_pending', 'orders/{order_id}', 'orders/{client_oid}', 'trade_fee', 'fills', 'algo', // public 'instruments', 'instruments/{instrument_id}/book', 'instruments/ticker', 'instruments/{instrument_id}/ticker', 'instruments/{instrument_id}/trades', 'instruments/{instrument_id}/candles', ], 'post': [ 'order_algo', 'orders', 'batch_orders', 'cancel_orders/{order_id}', 'cancel_orders/{client_oid}', 'cancel_batch_algos', 'cancel_batch_orders', ], }, 'margin': { 'get': [ 'accounts', 'accounts/{instrument_id}', 'accounts/{instrument_id}/ledger', 'accounts/availability', 'accounts/{instrument_id}/availability', 'accounts/borrowed', 'accounts/{instrument_id}/borrowed', 'orders', 'accounts/{instrument_id}/leverage', 'orders/{order_id}', 'orders/{client_oid}', 'orders_pending', 'fills', // public 'instruments/{instrument_id}/mark_price', ], 'post': [ 'accounts/borrow', 'accounts/repayment', 'orders', 'batch_orders', 'cancel_orders', 'cancel_orders/{order_id}', 'cancel_orders/{client_oid}', 'cancel_batch_orders', 'accounts/{instrument_id}/leverage', ], }, 'futures': { 'get': [ 'position', '{instrument_id}/position', 'accounts', 'accounts/{underlying}', 'accounts/{underlying}/leverage', 'accounts/{underlying}/ledger', 'order_algo/{instrument_id}', 'orders/{instrument_id}', 'orders/{instrument_id}/{order_id}', 'orders/{instrument_id}/{client_oid}', 'fills', 'trade_fee', 'accounts/{instrument_id}/holds', 'order_algo/{instrument_id}', // public 'instruments', 'instruments/{instrument_id}/book', 'instruments/ticker', 'instruments/{instrument_id}/ticker', 'instruments/{instrument_id}/trades', 'instruments/{instrument_id}/candles', 'instruments/{instrument_id}/index', 'rate', 'instruments/{instrument_id}/estimated_price', 'instruments/{instrument_id}/open_interest', 'instruments/{instrument_id}/price_limit', 'instruments/{instrument_id}/mark_price', 'instruments/{instrument_id}/liquidation', ], 'post': [ 'accounts/{underlying}/leverage', 'order', 'orders', 'cancel_order/{instrument_id}/{order_id}', 'cancel_order/{instrument_id}/{client_oid}', 'cancel_batch_orders/{instrument_id}', 'accounts/margin_mode', 'close_position', 'cancel_all', 'order_algo', 'cancel_algos', ], }, 'swap': { 'get': [ 'position', '{instrument_id}/position', 'accounts', '{instrument_id}/accounts', 'accounts/{instrument_id}/settings', 'accounts/{instrument_id}/ledger', 'orders/{instrument_id}', 'orders/{instrument_id}/{order_id}', 'orders/{instrument_id}/{client_oid}', 'fills', 'accounts/{instrument_id}/holds', 'trade_fee', 'order_algo/{instrument_id}', // public 'instruments', 'instruments/{instrument_id}/depth', 'instruments/ticker', 'instruments/{instrument_id}/ticker', 'instruments/{instrument_id}/trades', 'instruments/{instrument_id}/candles', 'instruments/{instrument_id}/index', 'rate', 'instruments/{instrument_id}/open_interest', 'instruments/{instrument_id}/price_limit', 'instruments/{instrument_id}/liquidation', 'instruments/{instrument_id}/funding_time', 'instruments/{instrument_id}/mark_price', 'instruments/{instrument_id}/historical_funding_rate', ], 'post': [ 'accounts/{instrument_id}/leverage', 'order', 'orders', 'cancel_order/{instrument_id}/{order_id}', 'cancel_order/{instrument_id}/{client_oid}', 'cancel_batch_orders/{instrument_id}', 'order_algo', 'cancel_algos', ], }, 'option': { 'get': [ 'accounts', '{underlying}/position', 'accounts/{underlying}', 'orders/{underlying}', 'fills/{underlying}', 'accounts/{underlying}/ledger', 'trade_fee', 'orders/{underlying}/{order_id}', 'orders/{underlying}/{client_oid}', // public 'underlying', 'instruments/{underlying}', 'instruments/{underlying}/summary', 'instruments/{underlying}/summary/{instrument_id}', 'instruments/{instrument_id}/book', 'instruments/{instrument_id}/trades', 'instruments/{instrument_id}/ticker', 'instruments/{instrument_id}/candles', ], 'post': [ 'order', 'orders', 'cancel_order/{underlying}/{order_id}', 'cancel_order/{underlying}/{client_oid}', 'cancel_batch_orders/{underlying}', 'amend_order/{underlying}', 'amend_batch_orders/{underlying}', ], }, 'index': { 'get': [ '{instrument_id}/constituents', ], }, }, 'fees': { 'trading': { 'taker': 0.0015, 'maker': 0.0010, }, 'spot': { 'taker': 0.0015, 'maker': 0.0010, }, 'futures': { 'taker': 0.0005, 'maker': 0.0002, }, 'swap': { 'taker': 0.00075, 'maker': 0.00020, }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'password': true, }, 'exceptions': { // http error codes // 400 Bad Request — Invalid request format // 401 Unauthorized — Invalid API Key // 403 Forbidden — You do not have access to the requested resource // 404 Not Found // 500 Internal Server Error — We had a problem with our server 'exact': { '1': ExchangeError, // { "code": 1, "message": "System error" } // undocumented 'failure to get a peer from the ring-balancer': ExchangeNotAvailable, // { "message": "failure to get a peer from the ring-balancer" } '4010': PermissionDenied, // { "code": 4010, "message": "For the security of your funds, withdrawals are not permitted within 24 hours after changing fund password / mobile number / Google Authenticator settings " } // common // '0': ExchangeError, // 200 successful,when the order placement / cancellation / operation is successful '4001': ExchangeError, // no data received in 30s '4002': ExchangeError, // Buffer full. cannot write data // -------------------------------------------------------- '30001': AuthenticationError, // { "code": 30001, "message": 'request header "OK_ACCESS_KEY" cannot be blank'} '30002': AuthenticationError, // { "code": 30002, "message": 'request header "OK_ACCESS_SIGN" cannot be blank'} '30003': AuthenticationError, // { "code": 30003, "message": 'request header "OK_ACCESS_TIMESTAMP" cannot be blank'} '30004': AuthenticationError, // { "code": 30004, "message": 'request header "OK_ACCESS_PASSPHRASE" cannot be blank'} '30005': InvalidNonce, // { "code": 30005, "message": "invalid OK_ACCESS_TIMESTAMP" } '30006': AuthenticationError, // { "code": 30006, "message": "invalid OK_ACCESS_KEY" } '30007': BadRequest, // { "code": 30007, "message": 'invalid Content_Type, please use "application/json" format'} '30008': RequestTimeout, // { "code": 30008, "message": "timestamp request expired" } '30009': ExchangeError, // { "code": 30009, "message": "system error" } '30010': AuthenticationError, // { "code": 30010, "message": "API validation failed" } '30011': PermissionDenied, // { "code": 30011, "message": "invalid IP" } '30012': AuthenticationError, // { "code": 30012, "message": "invalid authorization" } '30013': AuthenticationError, // { "code": 30013, "message": "invalid sign" } '30014': DDoSProtection, // { "code": 30014, "message": "request too frequent" } '30015': AuthenticationError, // { "code": 30015, "message": 'request header "OK_ACCESS_PASSPHRASE" incorrect'} '30016': ExchangeError, // { "code": 30015, "message": "you are using v1 apiKey, please use v1 endpoint. If you would like to use v3 endpoint, please subscribe to v3 apiKey" } '30017': ExchangeError, // { "code": 30017, "message": "apikey's broker id does not match" } '30018': ExchangeError, // { "code": 30018, "message": "apikey's domain does not match" } '30019': ExchangeNotAvailable, // { "code": 30019, "message": "Api is offline or unavailable" } '30020': BadRequest, // { "code": 30020, "message": "body cannot be blank" } '30021': BadRequest, // { "code": 30021, "message": "Json data format error" }, { "code": 30021, "message": "json data format error" } '30022': PermissionDenied, // { "code": 30022, "message": "Api has been frozen" } '30023': BadRequest, // { "code": 30023, "message": "{0} parameter cannot be blank" } '30024': BadSymbol, // {"code":30024,"message":"\"instrument_id\" is an invalid parameter"} '30025': BadRequest, // { "code": 30025, "message": "{0} parameter category error" } '30026': DDoSProtection, // { "code": 30026, "message": "requested too frequent" } '30027': AuthenticationError, // { "code": 30027, "message": "login failure" } '30028': PermissionDenied, // { "code": 30028, "message": "unauthorized execution" } '30029': AccountSuspended, // { "code": 30029, "message": "account suspended" } '30030': ExchangeError, // { "code": 30030, "message": "endpoint request failed. Please try again" } '30031': BadRequest, // { "code": 30031, "message": "token does not exist" } '30032': BadSymbol, // { "code": 30032, "message": "pair does not exist" } '30033': BadRequest, // { "code": 30033, "message": "exchange domain does not exist" } '30034': ExchangeError, // { "code": 30034, "message": "exchange ID does not exist" } '30035': ExchangeError, // { "code": 30035, "message": "trading is not supported in this website" } '30036': ExchangeError, // { "code": 30036, "message": "no relevant data" } '30037': ExchangeNotAvailable, // { "code": 30037, "message": "endpoint is offline or unavailable" } // '30038': AuthenticationError, // { "code": 30038, "message": "user does not exist" } '30038': OnMaintenance, // {"client_oid":"","code":"30038","error_code":"30038","error_message":"Matching engine is being upgraded. Please try in about 1 minute.","message":"Matching engine is being upgraded. Please try in about 1 minute.","order_id":"-1","result":false} // futures '32001': AccountSuspended, // { "code": 32001, "message": "futures account suspended" } '32002': PermissionDenied, // { "code": 32002, "message": "futures account does not exist" } '32003': CancelPending, // { "code": 32003, "message": "canceling, please wait" } '32004': ExchangeError, // { "code": 32004, "message": "you have no unfilled orders" } '32005': InvalidOrder, // { "code": 32005, "message": "max order quantity" } '32006': InvalidOrder, // { "code": 32006, "message": "the order price or trigger price exceeds USD 1 million" } '32007': InvalidOrder, // { "code": 32007, "message": "leverage level must be the same for orders on the same side of the contract" } '32008': InvalidOrder, // { "code": 32008, "message": "Max. positions to open (cross margin)" } '32009': InvalidOrder, // { "code": 32009, "message": "Max. positions to open (fixed margin)" } '32010': ExchangeError, // { "code": 32010, "message": "leverage cannot be changed with open positions" } '32011': ExchangeError, // { "code": 32011, "message": "futures status error" } '32012': ExchangeError, // { "code": 32012, "message": "futures order update error" } '32013': ExchangeError, // { "code": 32013, "message": "token type is blank" } '32014': ExchangeError, // { "code": 32014, "message": "your number of contracts closing is larger than the number of contracts available" } '32015': ExchangeError, // { "code": 32015, "message": "margin ratio is lower than 100% before opening positions" } '32016': ExchangeError, // { "code": 32016, "message": "margin ratio is lower than 100% after opening position" } '32017': ExchangeError, // { "code": 32017, "message": "no BBO" } '32018': ExchangeError, // { "code": 32018, "message": "the order quantity is less than 1, please try again" } '32019': ExchangeError, // { "code": 32019, "message": "the order price deviates from the price of the previous minute by more than 3%" } '32020': ExchangeError, // { "code": 32020, "message": "the price is not in the range of the price limit" } '32021': ExchangeError, // { "code": 32021, "message": "leverage error" } '32022': ExchangeError, // { "code": 32022, "message": "this function is not supported in your country or region according to the regulations" } '32023': ExchangeError, // { "code": 32023, "message": "this account has outstanding loan" } '32024': ExchangeError, // { "code": 32024, "message": "order cannot be placed during delivery" } '32025': ExchangeError, // { "code": 32025, "message": "order cannot be placed during settlement" } '32026': ExchangeError, // { "code": 32026, "message": "your account is restricted from opening positions" } '32027': ExchangeError, // { "code": 32027, "message": "cancelled over 20 orders" } '32028': ExchangeError, // { "code": 32028, "message": "account is suspended and liquidated" } '32029': ExchangeError, // { "code": 32029, "message": "order info does not exist" } '32030': InvalidOrder, // The order cannot be cancelled '32031': ArgumentsRequired, // client_oid or order_id is required. '32038': AuthenticationError, // User does not exist '32040': ExchangeError, // User have open contract orders or position '32044': ExchangeError, // { "code": 32044, "message": "The margin ratio after submitting this order is lower than the minimum requirement ({0}) for your tier." } '32045': ExchangeError, // String of commission over 1 million '32046': ExchangeError, // Each user can hold up to 10 trade plans at the same time '32047': ExchangeError, // system error '32048': InvalidOrder, // Order strategy track range error '32049': ExchangeError, // Each user can hold up to 10 track plans at the same time '32050': InvalidOrder, // Order strategy rang error '32051': InvalidOrder, // Order strategy ice depth error '32052': ExchangeError, // String of commission over 100 thousand '32053': ExchangeError, // Each user can hold up to 6 ice plans at the same time '32057': ExchangeError, // The order price is zero. Market-close-all function cannot be executed '32054': ExchangeError, // Trade not allow '32055': InvalidOrder, // cancel order error '32056': ExchangeError, // iceberg per order average should between {0}-{1} contracts '32058': ExchangeError, // Each user can hold up to 6 initiative plans at the same time '32059': InvalidOrder, // Total amount should exceed per order amount '32060': InvalidOrder, // Order strategy type error '32061': InvalidOrder, // Order strategy initiative limit error '32062': InvalidOrder, // Order strategy initiative range error '32063': InvalidOrder, // Order strategy initiative rate error '32064': ExchangeError, // Time Stringerval of orders should set between 5-120s '32065': ExchangeError, // Close amount exceeds the limit of Market-close-all (999 for BTC, and 9999 for the rest tokens) '32066': ExchangeError, // You have open orders. Please cancel all open orders before changing your leverage level. '32067': ExchangeError, // Account equity < required margin in this setting. Please adjust your leverage level again. '32068': ExchangeError, // The margin for this position will fall short of the required margin in this setting. Please adjust your leverage level or increase your margin to proceed. '32069': ExchangeError, // Target leverage level too low. Your account balance is insufficient to cover the margin required. Please adjust the leverage level again. '32070': ExchangeError, // Please check open position or unfilled order '32071': ExchangeError, // Your current liquidation mode does not support this action. '32072': ExchangeError, // The highest available margin for your order’s tier is {0}. Please edit your margin and place a new order. '32073': ExchangeError, // The action does not apply to the token '32074': ExchangeError, // The number of contracts of your position, open orders, and the current order has exceeded the maximum order limit of this asset. '32075': ExchangeError, // Account risk rate breach '32076': ExchangeError, // Liquidation of the holding position(s) at market price will require cancellation of all pending close orders of the contracts. '32077': ExchangeError, // Your margin for this asset in futures account is insufficient and the position has been taken over for liquidation. (You will not be able to place orders, close positions, transfer funds, or add margin during this period of time. Your account will be restored after the liquidation is complete.) '32078': ExchangeError, // Please cancel all open orders before switching the liquidation mode(Please cancel all open orders before switching the liquidation mode) '32079': ExchangeError, // Your open positions are at high risk.(Please add margin or reduce positions before switching the mode) '32080': ExchangeError, // Funds cannot be transferred out within 30 minutes after futures settlement '32083': ExchangeError, // The number of contracts should be a positive multiple of %%. Please place your order again // token and margin trading '33001': PermissionDenied, // { "code": 33001, "message": "margin account for this pair is not enabled yet" } '33002': AccountSuspended, // { "code": 33002, "message": "margin account for this pair is suspended" } '33003': InsufficientFunds, // { "code": 33003, "message": "no loan balance" } '33004': ExchangeError, // { "code": 33004, "message": "loan amount cannot be smaller than the minimum limit" } '33005': ExchangeError, // { "code": 33005, "message": "repayment amount must exceed 0" } '33006': ExchangeError, // { "code": 33006, "message": "loan order not found" } '33007': ExchangeError, // { "code": 33007, "message": "status not found" } '33008': ExchangeError, // { "code": 33008, "message": "loan amount cannot exceed the maximum limit" } '33009': ExchangeError, // { "code": 33009, "message": "user ID is blank" } '33010': ExchangeError, // { "code": 33010, "message": "you cannot cancel an order during session 2 of call auction" } '33011': ExchangeError, // { "code": 33011, "message": "no new market data" } '33012': ExchangeError, // { "code": 33012, "message": "order cancellation failed" } '33013': InvalidOrder, // { "code": 33013, "message": "order placement failed" } '33014': OrderNotFound, // { "code": 33014, "message": "order does not exist" } '33015': InvalidOrder, // { "code": 33015, "message": "exceeded maximum limit" } '33016': ExchangeError, // { "code": 33016, "message": "margin trading is not open for this token" } '33017': InsufficientFunds, // { "code": 33017, "message": "insufficient balance" } '33018': ExchangeError, // { "code": 33018, "message": "this parameter must be smaller than 1" } '33020': ExchangeError, // { "code": 33020, "message": "request not supported" } '33021': BadRequest, // { "code": 33021, "message": "token and the pair do not match" } '33022': InvalidOrder, // { "code": 33022, "message": "pair and the order do not match" } '33023': ExchangeError, // { "code": 33023, "message": "you can only place market orders during call auction" } '33024': InvalidOrder, // { "code": 33024, "message": "trading amount too small" } '33025': InvalidOrder, // { "code": 33025, "message": "base token amount is blank" } '33026': ExchangeError, // { "code": 33026, "message": "transaction completed" } '33027': InvalidOrder, // { "code": 33027, "message": "cancelled order or order cancelling" } '33028': InvalidOrder, // { "code": 33028, "message": "the decimal places of the trading price exceeded the limit" } '33029': InvalidOrder, // { "code": 33029, "message": "the decimal places of the trading size exceeded the limit" } '33034': ExchangeError, // { "code": 33034, "message": "You can only place limit order after Call Auction has started" } '33035': ExchangeError, // This type of order cannot be canceled(This type of order cannot be canceled) '33036': ExchangeError, // Exceeding the limit of entrust order '33037': ExchangeError, // The buy order price should be lower than 130% of the trigger price '33038': ExchangeError, // The sell order price should be higher than 70% of the trigger price '33039': ExchangeError, // The limit of callback rate is 0 < x <= 5% '33040': ExchangeError, // The trigger price of a buy order should be lower than the latest transaction price '33041': ExchangeError, // The trigger price of a sell order should be higher than the latest transaction price '33042': ExchangeError, // The limit of price variance is 0 < x <= 1% '33043': ExchangeError, // The total amount must be larger than 0 '33044': ExchangeError, // The average amount should be 1/1000 * total amount <= x <= total amount '33045': ExchangeError, // The price should not be 0, including trigger price, order price, and price limit '33046': ExchangeError, // Price variance should be 0 < x <= 1% '33047': ExchangeError, // Sweep ratio should be 0 < x <= 100% '33048': ExchangeError, // Per order limit: Total amount/1000 < x <= Total amount '33049': ExchangeError, // Total amount should be X > 0 '33050': ExchangeError, // Time interval should be 5 <= x <= 120s '33051': ExchangeError, // cancel order number not higher limit: plan and track entrust no more than 10, ice and time entrust no more than 6 '33059': BadRequest, // { "code": 33059, "message": "client_oid or order_id is required" } '33060': BadRequest, // { "code": 33060, "message": "Only fill in either parameter client_oid or order_id" } '33061': ExchangeError, // Value of a single market price order cannot exceed 100,000 USD '33062': ExchangeError, // The leverage ratio is too high. The borrowed position has exceeded the maximum position of this leverage ratio. Please readjust the leverage ratio '33063': ExchangeError, // Leverage multiple is too low, there is insufficient margin in the account, please readjust the leverage ratio '33064': ExchangeError, // The setting of the leverage ratio cannot be less than 2, please readjust the leverage ratio '33065': ExchangeError, // Leverage ratio exceeds maximum leverage ratio, please readjust leverage ratio // account '21009': ExchangeError, // Funds cannot be transferred out within 30 minutes after swap settlement(Funds cannot be transferred out within 30 minutes after swap settlement) '34001': PermissionDenied, // { "code": 34001, "message": "withdrawal suspended" } '34002': InvalidAddress, // { "code": 34002, "message": "please add a withdrawal address" } '34003': ExchangeError, // { "code": 34003, "message": "sorry, this token cannot be withdrawn to xx at the moment" } '34004': ExchangeError, // { "code": 34004, "message": "withdrawal fee is smaller than minimum limit" } '34005': ExchangeError, // { "code": 34005, "message": "withdrawal fee exceeds the maximum limit" } '34006': ExchangeError, // { "code": 34006, "message": "withdrawal amount is lower than the minimum limit" } '34007': ExchangeError, // { "code": 34007, "message": "withdrawal amount exceeds the maximum limit" } '34008': InsufficientFunds, // { "code": 34008, "message": "insufficient balance" } '34009': ExchangeError, // { "code": 34009, "message": "your withdrawal amount exceeds the daily limit" } '34010': ExchangeError, // { "code": 34010, "message": "transfer amount must be larger than 0" } '34011': ExchangeError, // { "code": 34011, "message": "conditions not met" } '34012': ExchangeError, // { "code": 34012, "message": "the minimum withdrawal amount for NEO is 1, and the amount must be an integer" } '34013': ExchangeError, // { "code": 34013, "message": "please transfer" } '34014': ExchangeError, // { "code": 34014, "message": "transfer limited" } '34015': ExchangeError, // { "code": 34015, "message": "subaccount does not exist" } '34016': PermissionDenied, // { "code": 34016, "message": "transfer suspended" } '34017': AccountSuspended, // { "code": 34017, "message": "account suspended" } '34018': AuthenticationError, // { "code": 34018, "message": "incorrect trades password" } '34019': PermissionDenied, // { "code": 34019, "message": "please bind your email before withdrawal" } '34020': PermissionDenied, // { "code": 34020, "message": "please bind your funds password before withdrawal" } '34021': InvalidAddress, // { "code": 34021, "message": "Not verified address" } '34022': ExchangeError, // { "code": 34022, "message": "Withdrawals are not available for sub accounts" } '34023': PermissionDenied, // { "code": 34023, "message": "Please enable futures trading before transferring your funds" } '34026': ExchangeError, // transfer too frequently(transfer too frequently) '34036': ExchangeError, // Parameter is incorrect, please refer to API documentation '34037': ExchangeError, // Get the sub-account balance interface, account type is not supported '34038': ExchangeError, // Since your C2C transaction is unusual, you are restricted from fund transfer. Please contact our customer support to cancel the restriction '34039': ExchangeError, // You are now restricted from transferring out your funds due to abnormal trades on C2C Market. Please transfer your fund on our website or app instead to verify your identity // swap '35001': ExchangeError, // { "code": 35001, "message": "Contract does not exist" } '35002': ExchangeError, // { "code": 35002, "message": "Contract settling" } '35003': ExchangeError, // { "code": 35003, "message": "Contract paused" } '35004': ExchangeError, // { "code": 35004, "message": "Contract pending settlement" } '35005': AuthenticationError, // { "code": 35005, "message": "User does not exist" } '35008': InvalidOrder, // { "code": 35008, "message": "Risk ratio too high" } '35010': InvalidOrder, // { "code": 35010, "message": "Position closing too large" } '35012': InvalidOrder, // { "code": 35012, "message": "Incorrect order size" } '35014': InvalidOrder, // { "code": 35014, "message": "Order price is not within limit" } '35015': InvalidOrder, // { "code": 35015, "message": "Invalid leverage level" } '35017': ExchangeError, // { "code": 35017, "message": "Open orders exist" } '35019': InvalidOrder, // { "code": 35019, "message": "Order size too large" } '35020': InvalidOrder, // { "code": 35020, "message": "Order price too high" } '35021': InvalidOrder, // { "code": 35021, "message": "Order size exceeded current tier limit" } '35022': ExchangeError, // { "code": 35022, "message": "Contract status error" } '35024': ExchangeError, // { "code": 35024, "message": "Contract not initialized" } '35025': InsufficientFunds, // { "code": 35025, "message": "No account balance" } '35026': ExchangeError, // { "code": 35026, "message": "Contract settings not initialized" } '35029': OrderNotFound, // { "code": 35029, "message": "Order does not exist" } '35030': InvalidOrder, // { "code": 35030, "message": "Order size too large" } '35031': InvalidOrder, // { "code": 35031, "message": "Cancel order size too large" } '35032': ExchangeError, // { "code": 35032, "message": "Invalid user status" } '35037': ExchangeError, // No last traded price in cache '35039': ExchangeError, // { "code": 35039, "message": "Open order quantity exceeds limit" } '35040': InvalidOrder, // {"error_message":"Invalid order type","result":"true","error_code":"35040","order_id":"-1"} '35044': ExchangeError, // { "code": 35044, "message": "Invalid order status" } '35046': InsufficientFunds, // { "code": 35046, "message": "Negative account balance" } '35047': InsufficientFunds, // { "code": 35047, "message": "Insufficient account balance" } '35048': ExchangeError, // { "code": 35048, "message": "User contract is frozen and liquidating" } '35049': InvalidOrder, // { "code": 35049, "message": "Invalid order type" } '35050': InvalidOrder, // { "code": 35050, "message": "Position settings are blank" } '35052': InsufficientFunds, // { "code": 35052, "message": "Insufficient cross margin" } '35053': ExchangeError, // { "code": 35053, "message": "Account risk too high" } '35055': InsufficientFunds, // { "code": 35055, "message": "Insufficient account balance" } '35057': ExchangeError, // { "code": 35057, "message": "No last traded price" } '35058': ExchangeError, // { "code": 35058, "message": "No limit" } '35059': BadRequest, // { "code": 35059, "message": "client_oid or order_id is required" } '35060': BadRequest, // { "code": 35060, "message": "Only fill in either parameter client_oid or order_id" } '35061': BadRequest, // { "code": 35061, "message": "Invalid instrument_id" } '35062': InvalidOrder, // { "code": 35062, "message": "Invalid match_price" } '35063': InvalidOrder, // { "code": 35063, "message": "Invalid order_size" } '35064': InvalidOrder, // { "code": 35064, "message": "Invalid client_oid" } '35066': InvalidOrder, // Order interval error '35067': InvalidOrder, // Time-weighted order ratio error '35068': InvalidOrder, // Time-weighted order range error '35069': InvalidOrder, // Time-weighted single transaction limit error '35070': InvalidOrder, // Algo order type error '35071': InvalidOrder, // Order total must be larger than single order limit '35072': InvalidOrder, // Maximum 6 unfulfilled time-weighted orders can be held at the same time '35073': InvalidOrder, // Order price is 0. Market-close-all not available '35074': InvalidOrder, // Iceberg order single transaction average error '35075': InvalidOrder, // Failed to cancel order '35076': InvalidOrder, // LTC 20x leverage. Not allowed to open position '35077': InvalidOrder, // Maximum 6 unfulfilled iceberg orders can be held at the same time '35078': InvalidOrder, // Order amount exceeded 100,000 '35079': InvalidOrder, // Iceberg order price variance error '35080': InvalidOrder, // Callback rate error '35081': InvalidOrder, // Maximum 10 unfulfilled trail orders can be held at the same time '35082': InvalidOrder, // Trail order callback rate error '35083': InvalidOrder, // Each user can only hold a maximum of 10 unfulfilled stop-limit orders at the same time '35084': InvalidOrder, // Order amount exceeded 1 million '35085': InvalidOrder, // Order amount is not in the correct range '35086': InvalidOrder, // Price exceeds 100 thousand '35087': InvalidOrder, // Price exceeds 100 thousand '35088': InvalidOrder, // Average amount error '35089': InvalidOrder, // Price exceeds 100 thousand '35090': ExchangeError, // No stop-limit orders available for cancelation '35091': ExchangeError, // No trail orders available for cancellation '35092': ExchangeError, // No iceberg orders available for cancellation '35093': ExchangeError, // No trail orders available for cancellation '35094': ExchangeError, // Stop-limit order last traded price error '35095': BadRequest, // Instrument_id error '35096': ExchangeError, // Algo order status error '35097': ExchangeError, // Order status and order ID cannot exist at the same time '35098': ExchangeError, // An order status or order ID must exist '35099': ExchangeError, // Algo order ID error // option '36001': BadRequest, // Invalid underlying index. '36002': BadRequest, // Instrument does not exist. '36005': ExchangeError, // Instrument status is invalid. '36101': AuthenticationError, // Account does not exist. '36102': PermissionDenied, // Account status is invalid. '36103': PermissionDenied, // Account is suspended due to ongoing liquidation. '36104': PermissionDenied, // Account is not enabled for options trading. '36105': PermissionDenied, // Please enable the account for option contract. '36106': PermissionDenied, // Funds cannot be transferred in or out, as account is suspended. '36107': PermissionDenied, // Funds cannot be transferred out within 30 minutes after option exercising or settlement. '36108': InsufficientFunds, // Funds cannot be transferred in or out, as equity of the account is less than zero. '36109': PermissionDenied, // Funds cannot be transferred in or out during option exercising or settlement. '36201': PermissionDenied, // New order function is blocked. '36202': PermissionDenied, // Account does not have permission to short option. '36203': InvalidOrder, // Invalid format for client_oid. '36204': ExchangeError, // Invalid format for request_id. '36205': BadRequest, // Instrument id does not match underlying index. '36206': BadRequest, // Order_id and client_oid can not be used at the same time. '36207': InvalidOrder, // Either order price or fartouch price must be present. '36208': InvalidOrder, // Either order price or size must be present. '36209': InvalidOrder, // Either order_id or client_oid must be present. '36210': InvalidOrder, // Either order_ids or client_oids must be present. '36211': InvalidOrder, // Exceeding max batch size for order submission. '36212': InvalidOrder, // Exceeding max batch size for oder cancellation. '36213': InvalidOrder, // Exceeding max batch size for order amendment. '36214': ExchangeError, // Instrument does not have valid bid/ask quote. '36216': OrderNotFound, // Order does not exist. '36217': InvalidOrder, // Order submission failed. '36218': InvalidOrder, // Order cancellation failed. '36219': InvalidOrder, // Order amendment failed. '36220': InvalidOrder, // Order is pending cancel. '36221': InvalidOrder, // Order qty is not valid multiple of lot size. '36222': InvalidOrder, // Order price is breaching highest buy limit. '36223': InvalidOrder, // Order price is breaching lowest sell limit. '36224': InvalidOrder, // Exceeding max order size. '36225': InvalidOrder, // Exceeding max open order count for instrument. '36226': InvalidOrder, // Exceeding max open order count for underlying. '36227': InvalidOrder, // Exceeding max open size across all orders for underlying '36228': InvalidOrder, // Exceeding max available qty for instrument. '36229': InvalidOrder, // Exceeding max available qty for underlying. '36230': InvalidOrder, // Exceeding max position limit for underlying. }, 'broad': { }, }, 'precisionMode': TICK_SIZE, 'options': { 'createMarketBuyOrderRequiresPrice': true, 'fetchMarkets': [ 'spot', 'futures', 'swap', 'option' ], 'defaultType': 'spot', // 'account', 'spot', 'margin', 'futures', 'swap', 'option' 'auth': { 'time': 'public', 'currencies': 'private', 'instruments': 'public', 'rate': 'public', '{instrument_id}/constituents': 'public', }, }, 'commonCurrencies': { // OKEX refers to ERC20 version of Aeternity (AEToken) 'AE': 'AET', // https://github.com/ccxt/ccxt/issues/4981 'HOT': 'Hydro Protocol', 'HSR': 'HC', 'MAG': 'Maggie', 'YOYO': 'YOYOW', 'WIN': 'WinToken', // https://github.com/ccxt/ccxt/issues/5701 }, }); } async fetchTime (params = {}) { const response = await this.generalGetTime (params); // // { // "iso": "2015-01-07T23:47:25.201Z", // "epoch": 1420674445.201 // } // return this.parse8601 (this.safeString (response, 'iso')); } async fetchMarkets (params = {}) { const types = this.safeValue (this.options, 'fetchMarkets'); let result = []; for (let i = 0; i < types.length; i++) { const markets = await this.fetchMarketsByType (types[i], params); result = this.arrayConcat (result, markets); } return result; } parseMarkets (markets) { const result = []; for (let i = 0; i < markets.length; i++) { result.push (this.parseMarket (markets[i])); } return result; } parseMarket (market) { // // spot markets // // { // base_currency: "EOS", // instrument_id: "EOS-OKB", // min_size: "0.01", // quote_currency: "OKB", // size_increment: "0.000001", // tick_size: "0.0001" // } // // futures markets // // { // instrument_id: "XRP-USD-200320", // underlying_index: "XRP", // quote_currency: "USD", // tick_size: "0.0001", // contract_val: "10", // listing: "2020-03-06", // delivery: "2020-03-20", // trade_increment: "1", // alias: "this_week", // underlying: "XRP-USD", // base_currency: "XRP", // settlement_currency: "XRP", // is_inverse: "true", // contract_val_currency: "USD", // } // // swap markets // // { // instrument_id: "BSV-USD-SWAP", // underlying_index: "BSV", // quote_currency: "USD", // coin: "BSV", // contract_val: "10", // listing: "2018-12-21T07:53:47.000Z", // delivery: "2020-03-14T08:00:00.000Z", // size_increment: "1", // tick_size: "0.01", // base_currency: "BSV", // underlying: "BSV-USD", // settlement_currency: "BSV", // is_inverse: "true", // contract_val_currency: "USD" // } // // options markets // // { // instrument_id: 'BTC-USD-200327-4000-C', // underlying: 'BTC-USD', // settlement_currency: 'BTC', // contract_val: '0.1000', // option_type: 'C', // strike: '4000', // tick_size: '0.0005', // lot_size: '1.0000', // listing: '2019-12-25T08:30:36.302Z', // delivery: '2020-03-27T08:00:00.000Z', // state: '2', // trading_start_time: '2019-12-25T08:30:36.302Z', // timestamp: '2020-03-13T08:05:09.456Z', // } // const id = this.safeString (market, 'instrument_id'); let marketType = 'spot'; let spot = true; let future = false; let swap = false; let option = false; let baseId = this.safeString (market, 'base_currency'); let quoteId = this.safeString (market, 'quote_currency'); const contractVal = this.safeFloat (market, 'contract_val'); if (contractVal !== undefined) { if ('option_type' in market) { marketType = 'option'; spot = false; option = true; const underlying = this.safeString (market, 'underlying'); const parts = underlying.split ('-'); baseId = this.safeString (parts, 0); quoteId = this.safeString (parts, 1); } else { marketType = 'swap'; spot = false; swap = true; const futuresAlias = this.safeString (market, 'alias'); if (futuresAlias !== undefined) { swap = false; future = true; marketType = 'futures'; baseId = this.safeString (market, 'underlying_index'); } } } const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = spot ? (base + '/' + quote) : id; const lotSize = this.safeFloat2 (market, 'lot_size', 'trade_increment'); const precision = { 'amount': this.safeFloat (market, 'size_increment', lotSize), 'price': this.safeFloat (market, 'tick_size'), }; const minAmount = this.safeFloat2 (market, 'min_size', 'base_min_size'); const active = true; const fees = this.safeValue2 (this.fees, marketType, 'trading', {}); return this.extend (fees, { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'type': marketType, 'spot': spot, 'futures': future, 'swap': swap, 'option': option, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': minAmount, 'max': undefined, }, 'price': { 'min': precision['price'], 'max': undefined, }, 'cost': { 'min': precision['price'], 'max': undefined, }, }, }); } async fetchMarketsByType (type, params = {}) { if (type === 'option') { const underlying = await this.optionGetUnderlying (params); let result = []; for (let i = 0; i < underlying.length; i++) { const response = await this.optionGetInstrumentsUnderlying ({ 'underlying': underlying[i], }); // // options markets // // [ // { // instrument_id: 'BTC-USD-200327-4000-C', // underlying: 'BTC-USD', // settlement_currency: 'BTC', // contract_val: '0.1000', // option_type: 'C', // strike: '4000', // tick_size: '0.0005', // lot_size: '1.0000', // listing: '2019-12-25T08:30:36.302Z', // delivery: '2020-03-27T08:00:00.000Z', // state: '2', // trading_start_time: '2019-12-25T08:30:36.302Z', // timestamp: '2020-03-13T08:05:09.456Z', // }, // ] // result = this.arrayConcat (result, response); } return this.parseMarkets (result); } else if ((type === 'spot') || (type === 'futures') || (type === 'swap')) { const method = type + 'GetInstruments'; const response = await this[method] (params); // // spot markets // // [ // { // base_currency: "EOS", // instrument_id: "EOS-OKB", // min_size: "0.01", // quote_currency: "OKB", // size_increment: "0.000001", // tick_size: "0.0001" // } // ] // // futures markets // // [ // { // instrument_id: "XRP-USD-200320", // underlying_index: "XRP", // quote_currency: "USD", // tick_size: "0.0001", // contract_val: "10", // listing: "2020-03-06", // delivery: "2020-03-20", // trade_increment: "1", // alias: "this_week", // underlying: "XRP-USD", // base_currency: "XRP", // settlement_currency: "XRP", // is_inverse: "true", // contract_val_currency: "USD", // } // ] // // swap markets // // [ // { // instrument_id: "BSV-USD-SWAP", // underlying_index: "BSV", // quote_currency: "USD", // coin: "BSV", // contract_val: "10", // listing: "2018-12-21T07:53:47.000Z", // delivery: "2020-03-14T08:00:00.000Z", // size_increment: "1", // tick_size: "0.01", // base_currency: "BSV", // underlying: "BSV-USD", // settlement_currency: "BSV", // is_inverse: "true", // contract_val_currency: "USD" // } // ] // return this.parseMarkets (response); } else { throw new NotSupported (this.id + ' fetchMarketsByType does not support market type ' + type); } } async fetchCurrencies (params = {}) { // has['fetchCurrencies'] is currently set to false // despite that their docs say these endpoints are public: // https://www.okex.com/api/account/v3/withdrawal/fee // https://www.okex.com/api/account/v3/currencies // it will still reply with { "code":30001, "message": "OK-ACCESS-KEY header is required" } // if you attempt to access it without authentication const response = await this.accountGetCurrencies (params); // // [ // { // name: '', // currency: 'BTC', // can_withdraw: '1', // can_deposit: '1', // min_withdrawal: '0.0100000000000000' // }, // ] // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'currency'); const code = this.safeCurrencyCode (id); const precision = 8; // default precision, todo: fix "magic constants" const name = this.safeString (currency, 'name'); const canDeposit = this.safeInteger (currency, 'can_deposit'); const canWithdraw = this.safeInteger (currency, 'can_withdraw'); const active = canDeposit && canWithdraw; result[code] = { 'id': id, 'code': code, 'info': currency, 'type': undefined, 'name': name, 'active': active, 'fee': undefined, // todo: redesign 'precision': precision, 'limits': { 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, 'withdraw': { 'min': this.safeFloat (currency, 'min_withdrawal'), 'max': undefined, }, }, }; } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let method = market['type'] + 'GetInstrumentsInstrumentId'; method += (market['type'] === 'swap') ? 'Depth' : 'Book'; const request = { 'instrument_id': market['id'], }; if (limit !== undefined) { request['size'] = limit; // max 200 } const response = await this[method] (this.extend (request, params)); // // { asks: [ ["0.02685268", "0.242571", "1"], // ["0.02685493", "0.164085", "1"], // ... // ["0.02779", "1.039", "1"], // ["0.027813", "0.0876", "1"] ], // bids: [ ["0.02684052", "10.371849", "1"], // ["0.02684051", "3.707", "4"], // ... // ["0.02634963", "0.132934", "1"], // ["0.02634962", "0.264838", "2"] ], // timestamp: "2018-12-17T20:24:16.159Z" } // const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); return this.parseOrderBook (response, timestamp); } parseTicker (ticker, market = undefined) { // // { best_ask: "0.02665472", // best_bid: "0.02665221", // instrument_id: "ETH-BTC", // product_id: "ETH-BTC", // last: "0.02665472", // ask: "0.02665472", // missing in the docs // bid: "0.02665221", // not mentioned in the docs // open_24h: "0.02645482", // high_24h: "0.02714633", // low_24h: "0.02614109", // base_volume_24h: "572298.901923", // timestamp: "2018-12-17T21:20:07.856Z", // quote_volume_24h: "15094.86831261" } // const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); let symbol = undefined; const marketId = this.safeString (ticker, 'instrument_id'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else if (marketId !== undefined) { const parts = marketId.split ('-'); const numParts = parts.length; if (numParts === 2) { const [ baseId, quoteId ] = parts; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } else { symbol = marketId; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); const open = this.safeFloat (ticker, 'open_24h'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high_24h'), 'low': this.safeFloat (ticker, 'low_24h'), 'bid': this.safeFloat (ticker, 'best_bid'), 'bidVolume': this.safeFloat (ticker, 'best_bid_size'), 'ask': this.safeFloat (ticker, 'best_ask'), 'askVolume': this.safeFloat (ticker, 'best_ask_size'), 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'base_volume_24h'), 'quoteVolume': this.safeFloat (ticker, 'quote_volume_24h'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const method = market['type'] + 'GetInstrumentsInstrumentIdTicker'; const request = { 'instrument_id': market['id'], }; const response = await this[method] (this.extend (request, params)); // // { best_ask: "0.02665472", // best_bid: "0.02665221", // instrument_id: "ETH-BTC", // product_id: "ETH-BTC", // last: "0.02665472", // ask: "0.02665472", // bid: "0.02665221", // open_24h: "0.02645482", // high_24h: "0.02714633", // low_24h: "0.02614109", // base_volume_24h: "572298.901923", // timestamp: "2018-12-17T21:20:07.856Z", // quote_volume_24h: "15094.86831261" } // return this.parseTicker (response); } async fetchTickersByType (type, symbols = undefined, params = {}) { await this.loadMarkets (); const method = type + 'GetInstrumentsTicker'; const response = await this[method] (params); const result = {}; for (let i = 0; i < response.length; i++) { const ticker = this.parseTicker (response[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchTickers (symbols = undefined, params = {}) { const defaultType = this.safeString2 (this.options, 'fetchTickers', 'defaultType'); const type = this.safeString (params, 'type', defaultType); return await this.fetchTickersByType (type, symbols, this.omit (params, 'type')); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // spot trades // // { // time: "2018-12-17T23:31:08.268Z", // timestamp: "2018-12-17T23:31:08.268Z", // trade_id: "409687906", // price: "0.02677805", // size: "0.923467", // side: "sell" // } // // futures trades, swap trades // // { // trade_id: "1989230840021013", // side: "buy", // price: "92.42", // qty: "184", // missing in swap markets // size: "5", // missing in futures markets // timestamp: "2018-12-17T23:26:04.613Z" // } // // fetchOrderTrades (private) // // spot trades, margin trades // // { // "created_at":"2019-03-15T02:52:56.000Z", // "exec_type":"T", // whether the order is taker or maker // "fee":"0.00000082", // "instrument_id":"BTC-USDT", // "ledger_id":"3963052721", // "liquidity":"T", // whether the order is taker or maker // "order_id":"2482659399697408", // "price":"3888.6", // "product_id":"BTC-USDT", // "side":"buy", // "size":"0.00055306", // "timestamp":"2019-03-15T02:52:56.000Z" // }, // // futures trades, swap trades // // { // "trade_id":"197429674631450625", // "instrument_id":"EOS-USD-SWAP", // "order_id":"6a-7-54d663a28-0", // "price":"3.633", // "order_qty":"1.0000", // "fee":"-0.000551", // "created_at":"2019-03-21T04:41:58.0Z", // missing in swap trades // "timestamp":"2019-03-25T05:56:31.287Z", // missing in futures trades // "exec_type":"M", // whether the order is taker or maker // "side":"short", // "buy" in futures trades // } // let symbol = undefined; const marketId = this.safeString (trade, 'instrument_id'); let base = undefined; let quote = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; base = market['base']; quote = market['quote']; } else if (marketId !== undefined) { const parts = marketId.split ('-'); const numParts = parts.length; if (numParts === 2) { const [ baseId, quoteId ] = parts; base = this.safeCurrencyCode (baseId); quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } else { symbol = marketId; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; base = market['base']; quote = market['quote']; } const timestamp = this.parse8601 (this.safeString2 (trade, 'timestamp', 'created_at')); const price = this.safeFloat (trade, 'price'); let amount = this.safeFloat2 (trade, 'size', 'qty'); amount = this.safeFloat (trade, 'order_qty', amount); let takerOrMaker = this.safeString2 (trade, 'exec_type', 'liquidity'); if (takerOrMaker === 'M') { takerOrMaker = 'maker'; } else if (takerOrMaker === 'T') { takerOrMaker = 'taker'; } const side = this.safeString (trade, 'side'); let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } const feeCost = this.safeFloat (trade, 'fee'); let fee = undefined; if (feeCost !== undefined) { const feeCurrency = (side === 'buy') ? base : quote; fee = { // fee is either a positive number (invitation rebate) // or a negative number (transaction fee deduction) // therefore we need to invert the fee // more about it https://github.com/ccxt/ccxt/issues/5909 'cost': -feeCost, 'currency': feeCurrency, }; } const orderId = this.safeString (trade, 'order_id'); return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': this.safeString2 (trade, 'trade_id', 'ledger_id'), 'order': orderId, 'type': undefined, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const method = market['type'] + 'GetInstrumentsInstrumentIdTrades'; if ((limit === undefined) || (limit > 100)) { limit = 100; // maximum = default = 100 } const request = { 'instrument_id': market['id'], 'limit': limit, // from: 'id', // to: 'id', }; const response = await this[method] (this.extend (request, params)); // // spot markets // // [ // { // time: "2018-12-17T23:31:08.268Z", // timestamp: "2018-12-17T23:31:08.268Z", // trade_id: "409687906", // price: "0.02677805", // size: "0.923467", // side: "sell" // } // ] // // futures markets, swap markets // // [ // { // trade_id: "1989230840021013", // side: "buy", // price: "92.42", // qty: "184", // missing in swap markets // size: "5", // missing in futures markets // timestamp: "2018-12-17T23:26:04.613Z" // } // ] // return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // spot markets // // { // close: "0.02684545", // high: "0.02685084", // low: "0.02683312", // open: "0.02683894", // time: "2018-12-17T20:28:00.000Z", // volume: "101.457222" // } // // futures markets // // [ // 1545072720000, // 0.3159, // 0.3161, // 0.3144, // 0.3149, // 22886, // 725179.26172331, // ] // if (Array.isArray (ohlcv)) { const numElements = ohlcv.length; const volumeIndex = (numElements > 6) ? 6 : 5; let timestamp = ohlcv[0]; if (typeof timestamp === 'string') { timestamp = this.parse8601 (timestamp); } return [ timestamp, // timestamp parseFloat (ohlcv[1]), // Open parseFloat (ohlcv[2]), // High parseFloat (ohlcv[3]), // Low parseFloat (ohlcv[4]), // Close // parseFloat (ohlcv[5]), // Quote Volume // parseFloat (ohlcv[6]), // Base Volume parseFloat (ohlcv[volumeIndex]), // Volume, okex will return base volume in the 7th element for future markets ]; } else { return [ this.parse8601 (this.safeString (ohlcv, 'time')), this.safeFloat (ohlcv, 'open'), // Open this.safeFloat (ohlcv, 'high'), // High this.safeFloat (ohlcv, 'low'), // Low this.safeFloat (ohlcv, 'close'), // Close this.safeFloat (ohlcv, 'volume'), // Base Volume ]; } } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const method = market['type'] + 'GetInstrumentsInstrumentIdCandles'; const request = { 'instrument_id': market['id'], 'granularity': this.timeframes[timeframe], }; const duration = this.parseTimeframe (timeframe); if (since !== undefined) { if (limit !== undefined) { request['end'] = this.iso8601 (this.sum (since, limit * duration * 1000)); } request['start'] = this.iso8601 (since); } else { const now = this.milliseconds (); if (limit !== undefined) { request['start'] = this.iso8601 (now - limit * duration * 1000); request['end'] = this.iso8601 (now); } } const response = await this[method] (this.extend (request, params)); // // spot markets // // [ { close: "0.02683401", // high: "0.02683401", // low: "0.02683401", // open: "0.02683401", // time: "2018-12-17T23:47:00.000Z", // volume: "0" }, // ... // { close: "0.02684545", // high: "0.02685084", // low: "0.02683312", // open: "0.02683894", // time: "2018-12-17T20:28:00.000Z", // volume: "101.457222" } ] // // futures // // [ [ 1545090660000, // 0.3171, // 0.3174, // 0.3171, // 0.3173, // 1648, // 51930.38579450868 ], // ... // [ 1545072720000, // 0.3159, // 0.3161, // 0.3144, // 0.3149, // 22886, // 725179.26172331 ] ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } parseAccountBalance (response) { // // account // // [ // { // balance: 0, // available: 0, // currency: "BTC", // hold: 0 // }, // { // balance: 0, // available: 0, // currency: "ETH", // hold: 0 // } // ] // // spot // // [ // { // frozen: "0", // hold: "0", // id: "2149632", // currency: "BTC", // balance: "0.0000000497717339", // available: "0.0000000497717339", // holds: "0" // }, // { // frozen: "0", // hold: "0", // id: "2149632", // currency: "ICN", // balance: "0.00000000925", // available: "0.00000000925", // holds: "0" // } // ] // const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'hold'); account['free'] = this.safeFloat (balance, 'available'); result[code] = account; } return this.parseBalance (result); } parseMarginBalance (response) { // // [ // { // "currency:BTC": { // "available":"0", // "balance":"0", // "borrowed":"0", // "can_withdraw":"0", // "frozen":"0", // "hold":"0", // "holds":"0", // "lending_fee":"0" // }, // "currency:USDT": { // "available":"100", // "balance":"100", // "borrowed":"0", // "can_withdraw":"100", // "frozen":"0", // "hold":"0", // "holds":"0", // "lending_fee":"0" // }, // "instrument_id":"BTC-USDT", // "liquidation_price":"0", // "product_id":"BTC-USDT", // "risk_rate":"" // }, // ] // const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const marketId = this.safeString (balance, 'instrument_id'); const market = this.safeValue (this.markets_by_id, marketId); let symbol = undefined; if (market === undefined) { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } else { symbol = market['symbol']; } const omittedBalance = this.omit (balance, [ 'instrument_id', 'liquidation_price', 'product_id', 'risk_rate', 'margin_ratio', 'maint_margin_ratio', 'tiers', ]); const keys = Object.keys (omittedBalance); const accounts = {}; for (let k = 0; k < keys.length; k++) { const key = keys[k]; const marketBalance = balance[key]; if (key.indexOf (':') >= 0) { const parts = key.split (':'); const currencyId = parts[1]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (marketBalance, 'balance'); account['used'] = this.safeFloat (marketBalance, 'hold'); account['free'] = this.safeFloat (marketBalance, 'available'); accounts[code] = account; } else { throw new NotSupported (this.id + ' margin balance response format has changed!'); } } result[symbol] = this.parseBalance (accounts); } return result; } parseFuturesBalance (response) { // // { // "info":{ // "eos":{ // "auto_margin":"0", // "contracts": [ // { // "available_qty":"40.37069445", // "fixed_balance":"0", // "instrument_id":"EOS-USD-190329", // "margin_for_unfilled":"0", // "margin_frozen":"0", // "realized_pnl":"0", // "unrealized_pnl":"0" // }, // { // "available_qty":"40.37069445", // "fixed_balance":"14.54895721", // "instrument_id":"EOS-USD-190628", // "margin_for_unfilled":"0", // "margin_frozen":"10.64042157", // "realized_pnl":"-3.90853564", // "unrealized_pnl":"-0.259" // }, // ], // "equity":"50.75220665", // "margin_mode":"fixed", // "total_avail_balance":"40.37069445" // }, // } // } // // their root field name is "info", so our info will contain their info const result = { 'info': response }; const info = this.safeValue (response, 'info', {}); const ids = Object.keys (info); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const code = this.safeCurrencyCode (id); const balance = this.safeValue (info, id, {}); const account = this.account (); // it may be incorrect to use total, free and used for swap accounts account['total'] = this.safeFloat (balance, 'equity'); account['free'] = this.safeFloat (balance, 'total_avail_balance'); result[code] = account; } return this.parseBalance (result); } parseSwapBalance (response) { // // { // "info": [ // { // "equity":"3.0139", // "fixed_balance":"0.0000", // "instrument_id":"EOS-USD-SWAP", // "margin":"0.5523", // "margin_frozen":"0.0000", // "margin_mode":"crossed", // "margin_ratio":"1.0913", // "realized_pnl":"-0.0006", // "timestamp":"2019-03-25T03:46:10.336Z", // "total_avail_balance":"3.0000", // "unrealized_pnl":"0.0145" // } // ] // } // // their root field name is "info", so our info will contain their info const result = { 'info': response }; const info = this.safeValue (response, 'info', []); for (let i = 0; i < info.length; i++) { const balance = info[i]; const marketId = this.safeString (balance, 'instrument_id'); let symbol = marketId; if (marketId in this.markets_by_id) { symbol = this.markets_by_id[marketId]['symbol']; } const account = this.account (); // it may be incorrect to use total, free and used for swap accounts account['total'] = this.safeFloat (balance, 'equity'); account['free'] = this.safeFloat (balance, 'total_avail_balance'); result[symbol] = account; } return this.parseBalance (result); } async fetchBalance (params = {}) { const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType'); const type = this.safeString (params, 'type', defaultType); if (type === undefined) { throw new ArgumentsRequired (this.id + " fetchBalance requires a type parameter (one of 'account', 'spot', 'margin', 'futures', 'swap')"); } await this.loadMarkets (); const suffix = (type === 'account') ? 'Wallet' : 'Accounts'; const method = type + 'Get' + suffix; const query = this.omit (params, 'type'); const response = await this[method] (query); // // account // // [ // { // balance: 0, // available: 0, // currency: "BTC", // hold: 0 // }, // { // balance: 0, // available: 0, // currency: "ETH", // hold: 0 // } // ] // // spot // // [ // { // frozen: "0", // hold: "0", // id: "2149632", // currency: "BTC", // balance: "0.0000000497717339", // available: "0.0000000497717339", // holds: "0" // }, // { // frozen: "0", // hold: "0", // id: "2149632", // currency: "ICN", // balance: "0.00000000925", // available: "0.00000000925", // holds: "0" // } // ] // // margin // // [ // { // "currency:BTC": { // "available":"0", // "balance":"0", // "borrowed":"0", // "can_withdraw":"0", // "frozen":"0", // "hold":"0", // "holds":"0", // "lending_fee":"0" // }, // "currency:USDT": { // "available":"100", // "balance":"100", // "borrowed":"0", // "can_withdraw":"100", // "frozen":"0", // "hold":"0", // "holds":"0", // "lending_fee":"0" // }, // "instrument_id":"BTC-USDT", // "liquidation_price":"0", // "product_id":"BTC-USDT", // "risk_rate":"" // }, // ] // // futures // // { // "info":{ // "eos":{ // "auto_margin":"0", // "contracts": [ // { // "available_qty":"40.37069445", // "fixed_balance":"0", // "instrument_id":"EOS-USD-190329", // "margin_for_unfilled":"0", // "margin_frozen":"0", // "realized_pnl":"0", // "unrealized_pnl":"0" // }, // { // "available_qty":"40.37069445", // "fixed_balance":"14.54895721", // "instrument_id":"EOS-USD-190628", // "margin_for_unfilled":"0", // "margin_frozen":"10.64042157", // "realized_pnl":"-3.90853564", // "unrealized_pnl":"-0.259" // }, // ], // "equity":"50.75220665", // "margin_mode":"fixed", // "total_avail_balance":"40.37069445" // }, // } // } // // swap // // { // "info": [ // { // "equity":"3.0139", // "fixed_balance":"0.0000", // "instrument_id":"EOS-USD-SWAP", // "margin":"0.5523", // "margin_frozen":"0.0000", // "margin_mode":"crossed", // "margin_ratio":"1.0913", // "realized_pnl":"-0.0006", // "timestamp":"2019-03-25T03:46:10.336Z", // "total_avail_balance":"3.0000", // "unrealized_pnl":"0.0145" // } // ] // } // return this.parseBalanceByType (type, response); } parseBalanceByType (type, response) { if ((type === 'account') || (type === 'spot')) { return this.parseAccountBalance (response); } else if (type === 'margin') { return this.parseMarginBalance (response); } else if (type === 'futures') { return this.parseFuturesBalance (response); } else if (type === 'swap') { return this.parseSwapBalance (response); } throw new NotSupported (this.id + " fetchBalance does not support the '" + type + "' type (the type must be one of 'account', 'spot', 'margin', 'futures', 'swap')"); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let request = { 'instrument_id': market['id'], // 'client_oid': 'abcdef1234567890', // [a-z0-9]{1,32} // 'order_type': '0', // 0: Normal limit order (Unfilled and 0 represent normal limit order) 1: Post only 2: Fill Or Kill 3: Immediatel Or Cancel }; let method = undefined; if (market['futures'] || market['swap']) { const size = market['futures'] ? this.numberToString (amount) : this.amountToPrecision (symbol, amount); request = this.extend (request, { 'type': type, // 1:open long 2:open short 3:close long 4:close short for futures 'size': size, 'price': this.priceToPrecision (symbol, price), // 'match_price': '0', // Order at best counter party price? (0:no 1:yes). The default is 0. If it is set as 1, the price parameter will be ignored. When posting orders at best bid price, order_type can only be 0 (regular order). }); if (market['futures']) { request['leverage'] = '10'; // or '20' } method = market['type'] + 'PostOrder'; } else { const marginTrading = this.safeString (params, 'margin_trading', '1'); // 1 = spot, 2 = margin request = this.extend (request, { 'side': side, 'type': type, // limit/market 'margin_trading': marginTrading, // 1 = spot, 2 = margin }); if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); request['size'] = this.amountToPrecision (symbol, amount); } else if (type === 'market') { // for market buy it requires the amount of quote currency to spend if (side === 'buy') { let notional = this.safeFloat (params, 'notional'); const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true); if (createMarketBuyOrderRequiresPrice) { if (price !== undefined) { if (notional === undefined) { notional = amount * price; } } else if (notional === undefined) { throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'amount' argument or in the 'notional' extra parameter (the exchange-specific behaviour)"); } } else { notional = (notional === undefined) ? amount : notional; } const precision = market['precision']['price']; request['notional'] = this.decimalToPrecision (notional, TRUNCATE, precision, this.precisionMode); } else { request['size'] = this.amountToPrecision (symbol, amount); } } method = (marginTrading === '2') ? 'marginPostOrders' : 'spotPostOrders'; } const response = await this[method] (this.extend (request, params)); // // { // "client_oid":"oktspot79", // "error_code":"", // "error_message":"", // "order_id":"2510789768709120", // "result":true // } // return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const type = market['type']; let method = type + 'PostCancelOrder'; const request = { 'instrument_id': market['id'], }; if (market['futures'] || market['swap']) { method += 'InstrumentId'; } else { method += 's'; } const clientOid = this.safeString (params, 'client_oid'); if (clientOid !== undefined) { method += 'ClientOid'; request['client_oid'] = clientOid; } else { method += 'OrderId'; request['order_id'] = id; } const query = this.omit (params, 'type'); const response = await this[method] (this.extend (request, query)); const result = ('result' in response) ? response : this.safeValue (response, market['id'], {}); // // spot, margin // // { // "btc-usdt": [ // { // "result":true, // "client_oid":"a123", // "order_id": "2510832677225473" // } // ] // } // // futures, swap // // { // "result": true, // "client_oid": "oktfuture10", // missing if requested by order_id // "order_id": "2517535534836736", // "instrument_id": "EOS-USD-190628" // } // return this.parseOrder (result, market); } parseOrderStatus (status) { const statuses = { '-2': 'failed', '-1': 'canceled', '0': 'open', '1': 'open', '2': 'closed', '3': 'open', '4': 'canceled', }; return this.safeString (statuses, status, status); } parseOrderSide (side) { const sides = { '1': 'buy', // open long '2': 'sell', // open short '3': 'sell', // close long '4': 'buy', // close short }; return this.safeString (sides, side, side); } parseOrder (order, market = undefined) { // // createOrder // // { // "client_oid":"oktspot79", // "error_code":"", // "error_message":"", // "order_id":"2510789768709120", // "result":true // } // // cancelOrder // // { // "result": true, // "client_oid": "oktfuture10", // missing if requested by order_id // "order_id": "2517535534836736", // // instrument_id is missing for spot/margin orders // // available in futures and swap orders only // "instrument_id": "EOS-USD-190628", // } // // fetchOrder, fetchOrdersByState, fetchOpenOrders, fetchClosedOrders // // // spot and margin orders // // { // "client_oid":"oktspot76", // "created_at":"2019-03-18T07:26:49.000Z", // "filled_notional":"3.9734", // "filled_size":"0.001", // filled_qty in futures and swap orders // "funds":"", // this is most likely the same as notional // "instrument_id":"BTC-USDT", // "notional":"", // "order_id":"2500723297813504", // "order_type":"0", // "price":"4013", // "product_id":"BTC-USDT", // missing in futures and swap orders // "side":"buy", // "size":"0.001", // "status":"filled", // "state": "2", // "timestamp":"2019-03-18T07:26:49.000Z", // "type":"limit" // } // // // futures and swap orders // // { // "instrument_id":"EOS-USD-190628", // "size":"10", // "timestamp":"2019-03-20T10:04:55.000Z", // "filled_qty":"10", // filled_size in spot and margin orders // "fee":"-0.00841043", // "order_id":"2512669605501952", // "price":"3.668", // "price_avg":"3.567", // missing in spot and margin orders // "status":"2", // "state": "2", // "type":"4", // "contract_val":"10", // "leverage":"10", // missing in swap, spot and margin orders // "client_oid":"", // "pnl":"1.09510794", // missing in swap, spo and margin orders // "order_type":"0" // } // const id = this.safeString (order, 'order_id'); const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); let side = this.safeString (order, 'side'); let type = this.safeString (order, 'type'); if ((side !== 'buy') && (side !== 'sell')) { side = this.parseOrderSide (type); } if ((type !== 'limit') && (type !== 'market')) { if ('pnl' in order) { type = 'futures'; } else { type = 'swap'; } } let symbol = undefined; const marketId = this.safeString (order, 'instrument_id'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { symbol = marketId; } if (market !== undefined) { if (symbol === undefined) { symbol = market['symbol']; } } let amount = this.safeFloat (order, 'size'); const filled = this.safeFloat2 (order, 'filled_size', 'filled_qty'); let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { amount = Math.max (amount, filled); remaining = Math.max (0, amount - filled); } } if (type === 'market') { remaining = 0; } let cost = this.safeFloat2 (order, 'filled_notional', 'funds'); const price = this.safeFloat (order, 'price'); let average = this.safeFloat (order, 'price_avg'); if (cost === undefined) { if (filled !== undefined && average !== undefined) { cost = average * filled; } } else { if ((average === undefined) && (filled !== undefined) && (filled > 0)) { average = cost / filled; } } const status = this.parseOrderStatus (this.safeString (order, 'state')); const feeCost = this.safeFloat (order, 'fee'); let fee = undefined; if (feeCost !== undefined) { const feeCurrency = undefined; fee = { 'cost': feeCost, 'currency': feeCurrency, }; } const clientOrderId = this.safeString (order, 'client_oid'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'average': average, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const defaultType = this.safeString2 (this.options, 'fetchOrder', 'defaultType', market['type']); const type = this.safeString (params, 'type', defaultType); if (type === undefined) { throw new ArgumentsRequired (this.id + " fetchOrder requires a type parameter (one of 'spot', 'margin', 'futures', 'swap')."); } const instrumentId = (market['futures'] || market['swap']) ? 'InstrumentId' : ''; let method = type + 'GetOrders' + instrumentId; const request = { 'instrument_id': market['id'], // 'client_oid': 'abcdef12345', // optional, [a-z0-9]{1,32} // 'order_id': id, }; const clientOid = this.safeString (params, 'client_oid'); if (clientOid !== undefined) { method += 'ClientOid'; request['client_oid'] = clientOid; } else { method += 'OrderId'; request['order_id'] = id; } const query = this.omit (params, 'type'); const response = await this[method] (this.extend (request, query)); // // spot, margin // // { // "client_oid":"oktspot70", // "created_at":"2019-03-15T02:52:56.000Z", // "filled_notional":"3.8886", // "filled_size":"0.001", // "funds":"", // "instrument_id":"BTC-USDT", // "notional":"", // "order_id":"2482659399697408", // "order_type":"0", // "price":"3927.3", // "product_id":"BTC-USDT", // "side":"buy", // "size":"0.001", // "status":"filled", // "state": "2", // "timestamp":"2019-03-15T02:52:56.000Z", // "type":"limit" // } // // futures, swap // // { // "instrument_id":"EOS-USD-190628", // "size":"10", // "timestamp":"2019-03-20T02:46:38.000Z", // "filled_qty":"10", // "fee":"-0.0080819", // "order_id":"2510946213248000", // "price":"3.712", // "price_avg":"3.712", // "status":"2", // "state": "2", // "type":"2", // "contract_val":"10", // "leverage":"10", // "client_oid":"", // missing in swap orders // "pnl":"0", // missing in swap orders // "order_type":"0" // } // return this.parseOrder (response); } async fetchOrdersByState (state, symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrdersByState requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const type = market['type']; const request = { 'instrument_id': market['id'], // '-2': failed, // '-1': cancelled, // '0': open , // '1': partially filled, // '2': fully filled, // '3': submitting, // '4': cancelling, // '6': incomplete(open+partially filled), // '7': complete(cancelled+fully filled), 'state': state, }; let method = type + 'GetOrders'; if (market['futures'] || market['swap']) { method += 'InstrumentId'; } const query = this.omit (params, 'type'); const response = await this[method] (this.extend (request, query)); // // spot, margin // // [ // // in fact, this documented API response does not correspond // // to their actual API response for spot markets // // OKEX v3 API returns a plain array of orders (see below) // [ // { // "client_oid":"oktspot76", // "created_at":"2019-03-18T07:26:49.000Z", // "filled_notional":"3.9734", // "filled_size":"0.001", // "funds":"", // "instrument_id":"BTC-USDT", // "notional":"", // "order_id":"2500723297813504", // "order_type":"0", // "price":"4013", // "product_id":"BTC-USDT", // "side":"buy", // "size":"0.001", // "status":"filled", // "state": "2", // "timestamp":"2019-03-18T07:26:49.000Z", // "type":"limit" // }, // ], // { // "before":"2500723297813504", // "after":"2500650881647616" // } // ] // // futures, swap // // { // "result":true, // missing in swap orders // "order_info": [ // { // "instrument_id":"EOS-USD-190628", // "size":"10", // "timestamp":"2019-03-20T10:04:55.000Z", // "filled_qty":"10", // "fee":"-0.00841043", // "order_id":"2512669605501952", // "price":"3.668", // "price_avg":"3.567", // "status":"2", // "state": "2", // "type":"4", // "contract_val":"10", // "leverage":"10", // missing in swap orders // "client_oid":"", // "pnl":"1.09510794", // missing in swap orders // "order_type":"0" // }, // ] // } // let orders = undefined; if (market['type'] === 'swap' || market['type'] === 'futures') { orders = this.safeValue (response, 'order_info', []); } else { orders = response; const responseLength = response.length; if (responseLength < 1) { return []; } // in fact, this documented API response does not correspond // to their actual API response for spot markets // OKEX v3 API returns a plain array of orders if (responseLength > 1) { const before = this.safeValue (response[1], 'before'); if (before !== undefined) { orders = response[0]; } } } return this.parseOrders (orders, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { // '-2': failed, // '-1': cancelled, // '0': open , // '1': partially filled, // '2': fully filled, // '3': submitting, // '4': cancelling, // '6': incomplete(open+partially filled), // '7': complete(cancelled+fully filled), return await this.fetchOrdersByState ('6', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { // '-2': failed, // '-1': cancelled, // '0': open , // '1': partially filled, // '2': fully filled, // '3': submitting, // '4': cancelling, // '6': incomplete(open+partially filled), // '7': complete(cancelled+fully filled), return await this.fetchOrdersByState ('7', symbol, since, limit, params); } parseDepositAddresses (addresses) { const result = {}; for (let i = 0; i < addresses.length; i++) { const address = this.parseDepositAddress (addresses[i]); const code = address['currency']; result[code] = address; } return result; } parseDepositAddress (depositAddress, currency = undefined) { // // { // address: '0x696abb81974a8793352cbd33aadcf78eda3cfdfa', // currency: 'eth' // tag: 'abcde12345', // will be missing if the token does not require a deposit tag // payment_id: 'abcde12345', // will not be returned if the token does not require a payment_id // // can_deposit: 1, // 0 or 1, documented but missing // // can_withdraw: 1, // 0 or 1, documented but missing // } // const address = this.safeString (depositAddress, 'address'); let tag = this.safeString2 (depositAddress, 'tag', 'payment_id'); tag = this.safeString (depositAddress, 'memo', tag); const currencyId = this.safeString (depositAddress, 'currency'); const code = this.safeCurrencyCode (currencyId); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': depositAddress, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.accountGetDepositAddress (this.extend (request, params)); // // [ // { // address: '0x696abb81974a8793352cbd33aadcf78eda3cfdfa', // currency: 'eth' // } // ] // const addresses = this.parseDepositAddresses (response); const address = this.safeValue (addresses, code); if (address === undefined) { throw new InvalidAddress (this.id + ' fetchDepositAddress cannot return nonexistent addresses, you should create withdrawal addresses with the exchange website first'); } return address; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); if (tag) { address = address + ':' + tag; } const fee = this.safeString (params, 'fee'); if (fee === undefined) { throw new ArgumentsRequired (this.id + " withdraw() requires a `fee` string parameter, network transaction fee must be ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set '0'. Withdrawing to external digital asset address requires network transaction fee."); } const request = { 'currency': currency['id'], 'to_address': address, 'destination': '4', // 2 = OKCoin International, 3 = OKEx 4 = others 'amount': this.numberToString (amount), 'fee': fee, // String. Network transaction fee ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set as 0. Withdrawal to external digital asset address requires network transaction fee. }; if ('password' in params) { request['trade_pwd'] = params['password']; } else if ('trade_pwd' in params) { request['trade_pwd'] = params['trade_pwd']; } else if (this.password) { request['trade_pwd'] = this.password; } const query = this.omit (params, [ 'fee', 'password', 'trade_pwd' ]); if (!('trade_pwd' in request)) { throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a password / trade_pwd parameter'); } const response = await this.accountPostWithdrawal (this.extend (request, query)); // // { // "amount":"0.1", // "withdrawal_id":"67485", // "currency":"btc", // "result":true // } // return { 'info': response, 'id': this.safeString (response, 'withdrawal_id'), }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let method = 'accountGetDepositHistory'; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['code'] = currency['code']; method += 'Currency'; } const response = await this[method] (this.extend (request, params)); return this.parseTransactions (response, currency, since, limit, params); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let method = 'accountGetWithdrawalHistory'; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['code'] = currency['code']; method += 'Currency'; } const response = await this[method] (this.extend (request, params)); return this.parseTransactions (response, currency, since, limit, params); } parseTransactionStatus (status) { // // deposit statuses // // { // '0': 'waiting for confirmation', // '1': 'confirmation account', // '2': 'recharge success' // } // // withdrawal statues // // { // '-3': 'pending cancel', // '-2': 'cancelled', // '-1': 'failed', // '0': 'pending', // '1': 'sending', // '2': 'sent', // '3': 'email confirmation', // '4': 'manual confirmation', // '5': 'awaiting identity confirmation' // } // const statuses = { '-3': 'pending', '-2': 'pending', '-1': 'failed', '0': 'pending', '1': 'pending', '2': 'ok', '3': 'pending', '4': 'pending', '5': 'pending', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // withdraw // // { // "amount":"0.1", // "withdrawal_id":"67485", // "currency":"btc", // "result":true // } // // fetchWithdrawals // // { // amount: "4.72100000", // withdrawal_id: "1729116", // fee: "0.01000000eth", // txid: "0xf653125bbf090bcfe4b5e8e7b8f586a9d87aa7de94598702758c0802b…", // currency: "ETH", // from: "7147338839", // to: "0x26a3CB49578F07000575405a57888681249c35Fd", // timestamp: "2018-08-17T07:03:42.000Z", // status: "2" // } // // fetchDeposits // // { // amount: "0.47847546", // txid: "1723573_3_0_0_WALLET", // currency: "BTC", // to: "", // timestamp: "2018-08-16T03:41:10.000Z", // status: "2" // } // let type = undefined; let id = undefined; let address = undefined; const withdrawalId = this.safeString (transaction, 'withdrawal_id'); const addressFrom = this.safeString (transaction, 'from'); const addressTo = this.safeString (transaction, 'to'); if (withdrawalId !== undefined) { type = 'withdrawal'; id = withdrawalId; address = addressTo; } else { // the payment_id will appear on new deposits but appears to be removed from the response after 2 months id = this.safeString (transaction, 'payment_id'); type = 'deposit'; address = addressTo; } const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId); const amount = this.safeFloat (transaction, 'amount'); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const txid = this.safeString (transaction, 'txid'); const timestamp = this.parse8601 (this.safeString (transaction, 'timestamp')); let feeCost = undefined; if (type === 'deposit') { feeCost = 0; } else { if (currencyId !== undefined) { const feeWithCurrencyId = this.safeString (transaction, 'fee'); if (feeWithCurrencyId !== undefined) { // https://github.com/ccxt/ccxt/pull/5748 const lowercaseCurrencyId = currencyId.toLowerCase (); const feeWithoutCurrencyId = feeWithCurrencyId.replace (lowercaseCurrencyId, ''); feeCost = parseFloat (feeWithoutCurrencyId); } } } // todo parse tags return { 'info': transaction, 'id': id, 'currency': code, 'amount': amount, 'addressFrom': addressFrom, 'addressTo': addressTo, 'address': address, 'tagFrom': undefined, 'tagTo': undefined, 'tag': undefined, 'status': status, 'type': type, 'updated': undefined, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': { 'currency': code, 'cost': feeCost, }, }; } parseMyTrade (pair, market = undefined) { // check that trading symbols match in both entries const first = pair[0]; const second = pair[1]; const firstMarketId = this.safeString (first, 'instrument_id'); const secondMarketId = this.safeString (second, 'instrument_id'); if (firstMarketId !== secondMarketId) { throw new NotSupported (this.id + ' parseMyTrade() received unrecognized response format, differing instrument_ids in one fill, the exchange API might have changed, paste your verbose output: https://github.com/ccxt/ccxt/wiki/FAQ#what-is-required-to-get-help'); } const marketId = firstMarketId; // determine the base and quote let quoteId = undefined; let symbol = undefined; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; quoteId = market['quoteId']; symbol = market['symbol']; } else { const parts = marketId.split ('-'); quoteId = this.safeString (parts, 1); symbol = marketId; } const id = this.safeString (first, 'trade_id'); const price = this.safeFloat (first, 'price'); // determine buy/sell side and amounts // get the side from either the first trade or the second trade let feeCost = this.safeFloat (first, 'fee'); const index = (feeCost !== 0) ? 0 : 1; const userTrade = this.safeValue (pair, index); const otherTrade = this.safeValue (pair, 1 - index); const receivedCurrencyId = this.safeString (userTrade, 'currency'); let side = undefined; let amount = undefined; let cost = undefined; if (receivedCurrencyId === quoteId) { side = 'sell'; amount = this.safeFloat (otherTrade, 'size'); cost = this.safeFloat (userTrade, 'size'); } else { side = 'buy'; amount = this.safeFloat (userTrade, 'size'); cost = this.safeFloat (otherTrade, 'size'); } feeCost = (feeCost !== 0) ? feeCost : this.safeFloat (second, 'fee'); const trade = this.safeValue (pair, index); // // simplified structures to show the underlying semantics // // // market/limit sell // // { // "currency":"USDT", // "fee":"-0.04647925", // ←--- fee in received quote currency // "price":"129.13", // ←------ price // "size":"30.98616393", // ←-- cost // }, // { // "currency":"ETH", // "fee":"0", // "price":"129.13", // "size":"0.23996099", // ←--- amount // }, // // // market/limit buy // // { // "currency":"ETH", // "fee":"-0.00036049", // ←--- fee in received base currency // "price":"129.16", // ←------ price // "size":"0.240322", // ←----- amount // }, // { // "currency":"USDT", // "fee":"0", // "price":"129.16", // "size":"31.03998952", // ←-- cost // } // const timestamp = this.parse8601 (this.safeString2 (trade, 'timestamp', 'created_at')); let takerOrMaker = this.safeString2 (trade, 'exec_type', 'liquidity'); if (takerOrMaker === 'M') { takerOrMaker = 'maker'; } else if (takerOrMaker === 'T') { takerOrMaker = 'taker'; } let fee = undefined; if (feeCost !== undefined) { const feeCurrencyId = this.safeString (userTrade, 'currency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { // fee is either a positive number (invitation rebate) // or a negative number (transaction fee deduction) // therefore we need to invert the fee // more about it https://github.com/ccxt/ccxt/issues/5909 'cost': -feeCost, 'currency': feeCurrencyCode, }; } const orderId = this.safeString (trade, 'order_id'); return { 'info': pair, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': undefined, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } parseMyTrades (trades, market = undefined, since = undefined, limit = undefined, params = {}) { const grouped = this.groupBy (trades, 'trade_id'); const tradeIds = Object.keys (grouped); const result = []; for (let i = 0; i < tradeIds.length; i++) { const tradeId = tradeIds[i]; const pair = grouped[tradeId]; // make sure it has exactly 2 trades, no more, no less const numTradesInPair = pair.length; if (numTradesInPair === 2) { const trade = this.parseMyTrade (pair); result.push (trade); } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { // okex actually returns ledger entries instead of fills here, so each fill in the order // is represented by two trades with opposite buy/sell sides, not one :\ // this aspect renders the 'fills' endpoint unusable for fetchOrderTrades // until either OKEX fixes the API or we workaround this on our side somehow if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); if ((limit !== undefined) && (limit > 100)) { limit = 100; } const request = { 'instrument_id': market['id'], // 'order_id': id, // string // 'after': '1', // return the page after the specified page number // 'before': '1', // return the page before the specified page number // 'limit': limit, // optional, number of results per request, default = maximum = 100 }; const defaultType = this.safeString2 (this.options, 'fetchMyTrades', 'defaultType'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); const method = type + 'GetFills'; const response = await this[method] (this.extend (request, query)); // // [ // // sell // { // "created_at":"2020-03-29T11:55:25.000Z", // "currency":"USDT", // "exec_type":"T", // "fee":"-0.04647925", // "instrument_id":"ETH-USDT", // "ledger_id":"10562924353", // "liquidity":"T", // "order_id":"4636470489136128", // "price":"129.13", // "product_id":"ETH-USDT", // "side":"buy", // "size":"30.98616393", // "timestamp":"2020-03-29T11:55:25.000Z", // "trade_id":"18551601" // }, // { // "created_at":"2020-03-29T11:55:25.000Z", // "currency":"ETH", // "exec_type":"T", // "fee":"0", // "instrument_id":"ETH-USDT", // "ledger_id":"10562924352", // "liquidity":"T", // "order_id":"4636470489136128", // "price":"129.13", // "product_id":"ETH-USDT", // "side":"sell", // "size":"0.23996099", // "timestamp":"2020-03-29T11:55:25.000Z", // "trade_id":"18551601" // }, // // buy // { // "created_at":"2020-03-29T11:55:16.000Z", // "currency":"ETH", // "exec_type":"T", // "fee":"-0.00036049", // "instrument_id":"ETH-USDT", // "ledger_id":"10562922669", // "liquidity":"T", // "order_id": "4636469894136832", // "price":"129.16", // "product_id":"ETH-USDT", // "side":"buy", // "size":"0.240322", // "timestamp":"2020-03-29T11:55:16.000Z", // "trade_id":"18551600" // }, // { // "created_at":"2020-03-29T11:55:16.000Z", // "currency":"USDT", // "exec_type":"T", // "fee":"0", // "instrument_id":"ETH-USDT", // "ledger_id":"10562922668", // "liquidity":"T", // "order_id":"4636469894136832", // "price":"129.16", // "product_id":"ETH-USDT", // "side":"sell", // "size":"31.03998952", // "timestamp":"2020-03-29T11:55:16.000Z", // "trade_id":"18551600" // } // ] // return this.parseMyTrades (response, market, since, limit, params); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { // 'instrument_id': market['id'], 'order_id': id, // 'after': '1', // return the page after the specified page number // 'before': '1', // return the page before the specified page number // 'limit': limit, // optional, number of results per request, default = maximum = 100 }; return await this.fetchMyTrades (symbol, since, limit, this.extend (request, params)); } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const defaultType = this.safeString2 (this.options, 'fetchLedger', 'defaultType'); const type = this.safeString (params, 'type', defaultType); const query = this.omit (params, 'type'); const suffix = (type === 'account') ? '' : 'Accounts'; let argument = ''; const request = { // 'from': 'id', // 'to': 'id', }; if (limit !== undefined) { request['limit'] = limit; } let currency = undefined; if ((type === 'spot') || (type === 'futures')) { if (code === undefined) { throw new ArgumentsRequired (this.id + " fetchLedger requires a currency code argument for '" + type + "' markets"); } argument = 'Currency'; currency = this.currency (code); request['currency'] = currency['id']; } else if ((type === 'margin') || (type === 'swap')) { if (code === undefined) { throw new ArgumentsRequired (this.id + " fetchLedger requires a code argument (a market symbol) for '" + type + "' markets"); } argument = 'InstrumentId'; const market = this.market (code); // we intentionally put a market inside here for the margin and swap ledgers currency = this.currency (market['base']); request['instrument_id'] = market['id']; // // if (type === 'margin') { // // // // 3. Borrow // // 4. Repayment // // 5. Interest // // 7. Buy // // 8. Sell // // 9. From capital account // // 10. From C2C // // 11. From Futures // // 12. From Spot // // 13. From ETT // // 14. To capital account // // 15. To C2C // // 16. To Spot // // 17. To Futures // // 18. To ETT // // 19. Mandatory Repayment // // 20. From Piggybank // // 21. To Piggybank // // 22. From Perpetual // // 23. To Perpetual // // 24. Liquidation Fee // // 54. Clawback // // 59. Airdrop Return. // // // request['type'] = 'number'; // All types will be returned if this filed is left blank // } // } else if (type === 'account') { if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } // // // // // 1. deposit // // 2. withdrawal // // 13. cancel withdrawal // // 18. into futures account // // 19. out of futures account // // 20. into sub account // // 21. out of sub account // // 28. claim // // 29. into ETT account // // 30. out of ETT account // // 31. into C2C account // // 32. out of C2C account // // 33. into margin account // // 34. out of margin account // // 37. into spot account // // 38. out of spot account // // // request['type'] = 'number'; // } else { throw new NotSupported (this.id + " fetchLedger does not support the '" + type + "' type (the type must be one of 'account', 'spot', 'margin', 'futures', 'swap')"); } const method = type + 'Get' + suffix + argument + 'Ledger'; const response = await this[method] (this.extend (request, query)); // // transfer funds transfer in/out // trade funds moved as a result of a trade, spot and margin accounts only // rebate fee rebate as per fee schedule, spot and margin accounts only // match open long/open short/close long/close short (futures) or a change in the amount because of trades (swap) // fee fee, futures only // settlement settlement/clawback/settle long/settle short // liquidation force close long/force close short/deliver close long/deliver close short // funding funding fee, swap only // margin a change in the amount after adjusting margin, swap only // // account // // [ // { // "amount":0.00051843, // "balance":0.00100941, // "currency":"BTC", // "fee":0, // "ledger_id":8987285, // "timestamp":"2018-10-12T11:01:14.000Z", // "typename":"Get from activity" // } // ] // // spot // // [ // { // "timestamp":"2019-03-18T07:08:25.000Z", // "ledger_id":"3995334780", // "created_at":"2019-03-18T07:08:25.000Z", // "currency":"BTC", // "amount":"0.0009985", // "balance":"0.0029955", // "type":"trade", // "details":{ // "instrument_id":"BTC-USDT", // "order_id":"2500650881647616", // "product_id":"BTC-USDT" // } // } // ] // // margin // // [ // [ // { // "created_at":"2019-03-20T03:45:05.000Z", // "ledger_id":"78918186", // "timestamp":"2019-03-20T03:45:05.000Z", // "currency":"EOS", // "amount":"0", // ? // "balance":"0.59957711", // "type":"transfer", // "details":{ // "instrument_id":"EOS-USDT", // "order_id":"787057", // "product_id":"EOS-USDT" // } // } // ], // { // "before":"78965766", // "after":"78918186" // } // ] // // futures // // [ // { // "ledger_id":"2508090544914461", // "timestamp":"2019-03-19T14:40:24.000Z", // "amount":"-0.00529521", // "balance":"0", // "currency":"EOS", // "type":"fee", // "details":{ // "order_id":"2506982456445952", // "instrument_id":"EOS-USD-190628" // } // } // ] // // swap // // [ // { // "amount":"0.004742", // "fee":"-0.000551", // "type":"match", // "instrument_id":"EOS-USD-SWAP", // "ledger_id":"197429674941902848", // "timestamp":"2019-03-25T05:56:31.286Z" // }, // ] // const isArray = Array.isArray (response[0]); const isMargin = (type === 'margin'); const entries = (isMargin && isArray) ? response[0] : response; return this.parseLedger (entries, currency, since, limit); } parseLedgerEntryType (type) { const types = { 'transfer': 'transfer', // // funds transfer in/out 'trade': 'trade', // funds moved as a result of a trade, spot and margin accounts only 'rebate': 'rebate', // fee rebate as per fee schedule, spot and margin accounts only 'match': 'trade', // open long/open short/close long/close short (futures) or a change in the amount because of trades (swap) 'fee': 'fee', // fee, futures only 'settlement': 'trade', // settlement/clawback/settle long/settle short 'liquidation': 'trade', // force close long/force close short/deliver close long/deliver close short 'funding': 'fee', // funding fee, swap only 'margin': 'margin', // a change in the amount after adjusting margin, swap only }; return this.safeString (types, type, type); } parseLedgerEntry (item, currency = undefined) { // // // account // // { // "amount":0.00051843, // "balance":0.00100941, // "currency":"BTC", // "fee":0, // "ledger_id":8987285, // "timestamp":"2018-10-12T11:01:14.000Z", // "typename":"Get from activity" // } // // spot // // { // "timestamp":"2019-03-18T07:08:25.000Z", // "ledger_id":"3995334780", // "created_at":"2019-03-18T07:08:25.000Z", // "currency":"BTC", // "amount":"0.0009985", // "balance":"0.0029955", // "type":"trade", // "details":{ // "instrument_id":"BTC-USDT", // "order_id":"2500650881647616", // "product_id":"BTC-USDT" // } // } // // margin // // { // "created_at":"2019-03-20T03:45:05.000Z", // "ledger_id":"78918186", // "timestamp":"2019-03-20T03:45:05.000Z", // "currency":"EOS", // "amount":"0", // ? // "balance":"0.59957711", // "type":"transfer", // "details":{ // "instrument_id":"EOS-USDT", // "order_id":"787057", // "product_id":"EOS-USDT" // } // } // // futures // // { // "ledger_id":"2508090544914461", // "timestamp":"2019-03-19T14:40:24.000Z", // "amount":"-0.00529521", // "balance":"0", // "currency":"EOS", // "type":"fee", // "details":{ // "order_id":"2506982456445952", // "instrument_id":"EOS-USD-190628" // } // } // // swap // // { // "amount":"0.004742", // "fee":"-0.000551", // "type":"match", // "instrument_id":"EOS-USD-SWAP", // "ledger_id":"197429674941902848", // "timestamp":"2019-03-25T05:56:31.286Z" // }, // const id = this.safeString (item, 'ledger_id'); const account = undefined; const details = this.safeValue (item, 'details', {}); const referenceId = this.safeString (details, 'order_id'); const referenceAccount = undefined; const type = this.parseLedgerEntryType (this.safeString (item, 'type')); const code = this.safeCurrencyCode (this.safeString (item, 'currency'), currency); const amount = this.safeFloat (item, 'amount'); const timestamp = this.parse8601 (this.safeString (item, 'timestamp')); const fee = { 'cost': this.safeFloat (item, 'fee'), 'currency': code, }; const before = undefined; const after = this.safeFloat (item, 'balance'); const status = 'ok'; return { 'info': item, 'id': id, 'account': account, 'referenceId': referenceId, 'referenceAccount': referenceAccount, 'type': type, 'currency': code, 'amount': amount, 'before': before, // balance before 'after': after, // balance after 'status': status, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': fee, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const isArray = Array.isArray (params); let request = '/api/' + api + '/' + this.version + '/'; request += isArray ? path : this.implodeParams (path, params); const query = isArray ? params : this.omit (params, this.extractParams (path)); let url = this.implodeParams (this.urls['api']['rest'], { 'hostname': this.hostname }) + request; const type = this.getPathAuthenticationType (path); if (type === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (type === 'private') { this.checkRequiredCredentials (); const timestamp = this.iso8601 (this.milliseconds ()); headers = { 'OK-ACCESS-KEY': this.apiKey, 'OK-ACCESS-PASSPHRASE': this.password, 'OK-ACCESS-TIMESTAMP': timestamp, // 'OK-FROM': '', // 'OK-TO': '', // 'OK-LIMIT': '', }; let auth = timestamp + method + request; if (method === 'GET') { if (Object.keys (query).length) { const urlencodedQuery = '?' + this.urlencode (query); url += urlencodedQuery; auth += urlencodedQuery; } } else { if (isArray || Object.keys (query).length) { body = this.json (query); auth += body; } headers['Content-Type'] = 'application/json'; } const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); headers['OK-ACCESS-SIGN'] = this.decode (signature); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } getPathAuthenticationType (path) { // https://github.com/ccxt/ccxt/issues/6651 // a special case to handle the optionGetUnderlying interefering with // other endpoints containing this keyword if (path === 'underlying') { return 'public'; } const auth = this.safeValue (this.options, 'auth', {}); const key = this.findBroadlyMatchedKey (auth, path); return this.safeString (auth, key, 'private'); } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { const feedback = this.id + ' ' + body; if (code === 503) { // {"message":"name resolution failed"} throw new ExchangeNotAvailable (feedback); } if (!response) { return; // fallback to default error handler } const message = this.safeString (response, 'message'); const errorCode = this.safeString2 (response, 'code', 'error_code'); if (message !== undefined) { this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); const nonEmptyMessage = (message !== ''); const nonZeroErrorCode = (errorCode !== undefined) && (errorCode !== '0'); if (nonZeroErrorCode || nonEmptyMessage) { throw new ExchangeError (feedback); // unknown message } } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],115:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class paymium extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'paymium', 'name': 'Paymium', 'countries': [ 'FR', 'EU' ], 'rateLimit': 2000, 'version': 'v1', 'has': { 'CORS': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27790564-a945a9d4-5ff9-11e7-9d2d-b635763f2f24.jpg', 'api': 'https://paymium.com/api', 'www': 'https://www.paymium.com', 'fees': 'https://www.paymium.com/page/help/fees', 'doc': [ 'https://github.com/Paymium/api-documentation', 'https://www.paymium.com/page/developers', ], 'referral': 'https://www.paymium.com/page/sign-up?referral=eDAzPoRQFMvaAB8sf-qj', }, 'api': { 'public': { 'get': [ 'countries', 'data/{id}/ticker', 'data/{id}/trades', 'data/{id}/depth', 'bitcoin_charts/{id}/trades', 'bitcoin_charts/{id}/depth', ], }, 'private': { 'get': [ 'merchant/get_payment/{uuid}', 'user', 'user/addresses', 'user/addresses/{btc_address}', 'user/orders', 'user/orders/{uuid}', 'user/price_alerts', ], 'post': [ 'user/orders', 'user/addresses', 'user/payment_requests', 'user/price_alerts', 'merchant/create_payment', ], 'delete': [ 'user/orders/{uuid}/cancel', 'user/price_alerts/{id}', ], }, }, 'markets': { 'BTC/EUR': { 'id': 'eur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'btc', 'quoteId': 'eur' }, }, 'fees': { 'trading': { 'maker': 0.002, 'taker': 0.002, }, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetUser (params); const result = { 'info': response }; const currencies = Object.keys (this.currencies); for (let i = 0; i < currencies.length; i++) { const code = currencies[i]; const currencyId = this.currencyId (code); const free = 'balance_' + currencyId; if (free in response) { const account = this.account (); const used = 'locked_' + currencyId; account['free'] = this.safeFloat (response, free); account['used'] = this.safeFloat (response, used); result[code] = account; } } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const response = await this.publicGetDataIdDepth (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); } async fetchTicker (symbol, params = {}) { const request = { 'id': this.marketId (symbol), }; const ticker = await this.publicGetDataIdTicker (this.extend (request, params)); const timestamp = this.safeTimestamp (ticker, 'at'); const vwap = this.safeFloat (ticker, 'vwap'); const baseVolume = this.safeFloat (ticker, 'volume'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'price'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': this.safeFloat (ticker, 'variation'), 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } parseTrade (trade, market) { const timestamp = this.safeTimestamp (trade, 'created_at_int'); const id = this.safeString (trade, 'uuid'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const side = this.safeString (trade, 'side'); const price = this.safeFloat (trade, 'price'); const amountField = 'traded_' + market['base'].toLowerCase (); const amount = this.safeFloat (trade, amountField); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } return { 'info': trade, 'id': id, 'order': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; const response = await this.publicGetDataIdTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'type': this.capitalize (type) + 'Order', 'currency': this.marketId (symbol), 'direction': side, 'amount': amount, }; if (type !== 'market') { request['price'] = price; } const response = await this.privatePostUserOrders (this.extend (request, params)); return { 'info': response, 'id': response['uuid'], }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'uuid': id, }; return await this.privateDeleteUserOrdersUuidCancel (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); let auth = nonce + url; headers = { 'Api-Key': this.apiKey, 'Api-Nonce': nonce, }; if (method === 'POST') { if (Object.keys (query).length) { body = this.json (query); auth += body; headers['Content-Type'] = 'application/json'; } } else { if (Object.keys (query).length) { const queryString = this.urlencode (query); auth += queryString; url += '?' + queryString; } } headers['Api-Signature'] = this.hmac (this.encode (auth), this.encode (this.secret)); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const response = await this.fetch2 (path, api, method, params, headers, body); if ('errors' in response) { throw new ExchangeError (this.id + ' ' + this.json (response)); } return response; } }; },{"./base/Exchange":7,"./base/errors":9}],116:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ExchangeNotAvailable, RequestTimeout, AuthenticationError, PermissionDenied, DDoSProtection, InsufficientFunds, OrderNotFound, OrderNotCached, InvalidOrder, AccountSuspended, CancelPending, InvalidNonce, OnMaintenance } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class poloniex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'poloniex', 'name': 'Poloniex', 'countries': [ 'US' ], 'rateLimit': 1000, // up to 6 calls per second 'certified': true, // 2019-06-07 'pro': true, 'has': { 'CORS': false, 'createDepositAddress': true, 'createMarketOrder': false, 'editOrder': true, 'fetchClosedOrders': 'emulated', 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrder': true, // true endpoint for a single open order 'fetchOpenOrders': true, // true endpoint for open orders 'fetchOrder': 'emulated', // no endpoint for a single open-or-closed order (just for an open order only) 'fetchOrderBooks': true, 'fetchOrders': 'emulated', // no endpoint for open-or-closed orders (just for open orders only) 'fetchOrderStatus': 'emulated', // no endpoint for status of a single open-or-closed order (just for open orders only) 'fetchOrderTrades': true, // true endpoint for trades of a single open or closed order 'fetchTickers': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'fetchTransactions': true, 'fetchWithdrawals': true, 'cancelAllOrders': true, 'withdraw': true, }, 'timeframes': { '5m': 300, '15m': 900, '30m': 1800, '2h': 7200, '4h': 14400, '1d': 86400, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg', 'api': { 'public': 'https://poloniex.com/public', 'private': 'https://poloniex.com/tradingApi', }, 'www': 'https://www.poloniex.com', 'doc': 'https://docs.poloniex.com', 'fees': 'https://poloniex.com/fees', 'referral': 'https://www.poloniex.com/?utm_source=ccxt&utm_medium=web', }, 'api': { 'public': { 'get': [ 'return24hVolume', 'returnChartData', 'returnCurrencies', 'returnLoanOrders', 'returnOrderBook', 'returnTicker', 'returnTradeHistory', ], }, 'private': { 'post': [ 'buy', 'cancelLoanOffer', 'cancelOrder', 'cancelAllOrders', 'closeMarginPosition', 'createLoanOffer', 'generateNewAddress', 'getMarginPosition', 'marginBuy', 'marginSell', 'moveOrder', 'returnActiveLoans', 'returnAvailableAccountBalances', 'returnBalances', 'returnCompleteBalances', 'returnDepositAddresses', 'returnDepositsWithdrawals', 'returnFeeInfo', 'returnLendingHistory', 'returnMarginAccountSummary', 'returnOpenLoanOffers', 'returnOpenOrders', 'returnOrderTrades', 'returnOrderStatus', 'returnTradableBalances', 'returnTradeHistory', 'sell', 'toggleAutoRenew', 'transferBalance', 'withdraw', ], }, }, 'fees': { 'trading': { // starting from Jan 8 2020 'maker': 0.0009, 'taker': 0.0009, }, 'funding': {}, }, 'limits': { 'amount': { 'min': 0.000001, 'max': 1000000000, }, 'price': { 'min': 0.00000001, 'max': 1000000000, }, 'cost': { 'min': 0.00000000, 'max': 1000000000, }, }, 'precision': { 'amount': 8, 'price': 8, }, 'commonCurrencies': { 'AIR': 'AirCoin', 'APH': 'AphroditeCoin', 'BCC': 'BTCtalkcoin', 'BDG': 'Badgercoin', 'BTM': 'Bitmark', 'CON': 'Coino', 'GOLD': 'GoldEagles', 'GPUC': 'GPU', 'HOT': 'Hotcoin', 'ITC': 'Information Coin', 'PLX': 'ParallaxCoin', 'KEY': 'KEYCoin', 'STR': 'XLM', 'SOC': 'SOCC', 'XAP': 'API Coin', }, 'options': { 'limits': { 'cost': { 'min': { 'BTC': 0.0001, 'ETH': 0.0001, 'XMR': 0.0001, 'USDT': 1.0, }, }, }, }, 'exceptions': { 'exact': { 'You may only place orders that reduce your position.': InvalidOrder, 'Invalid order number, or you are not the person who placed the order.': OrderNotFound, 'Permission denied': PermissionDenied, 'Connection timed out. Please try again.': RequestTimeout, 'Internal error. Please try again.': ExchangeNotAvailable, 'Currently in maintenance mode.': OnMaintenance, 'Order not found, or you are not the person who placed it.': OrderNotFound, 'Invalid API key/secret pair.': AuthenticationError, 'Please do not make more than 8 API calls per second.': DDoSProtection, 'Rate must be greater than zero.': InvalidOrder, // {"error":"Rate must be greater than zero."} }, 'broad': { 'Total must be at least': InvalidOrder, // {"error":"Total must be at least 0.0001."} 'This account is frozen.': AccountSuspended, 'Not enough': InsufficientFunds, 'Nonce must be greater': InvalidNonce, 'You have already called cancelOrder or moveOrder on this order.': CancelPending, 'Amount must be at least': InvalidOrder, // {"error":"Amount must be at least 0.000001."} 'is either completed or does not exist': InvalidOrder, // {"error":"Order 587957810791 is either completed or does not exist."} }, }, }); } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; let key = 'quote'; const rate = market[takerOrMaker]; let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); if (side === 'sell') { cost *= price; } else { key = 'base'; } return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': parseFloat (this.feeToPrecision (symbol, cost)), }; } parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { return [ this.safeTimestamp (ohlcv, 'date'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'quoteVolume'), ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPair': market['id'], 'period': this.timeframes[timeframe], }; if (since === undefined) { request['end'] = this.seconds (); if (limit === undefined) { request['start'] = request['end'] - this.parseTimeframe ('1w'); // max range = 1 week } else { request['start'] = request['end'] - limit * this.parseTimeframe (timeframe); } } else { request['start'] = parseInt (since / 1000); if (limit !== undefined) { const end = this.sum (request['start'], limit * this.parseTimeframe (timeframe)); request['end'] = end; } } const response = await this.publicGetReturnChartData (this.extend (request, params)); return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchMarkets (params = {}) { const markets = await this.publicGetReturnTicker (params); const keys = Object.keys (markets); const result = []; for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = markets[id]; const [ quoteId, baseId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const limits = this.extend (this.limits, { 'cost': { 'min': this.safeValue (this.options['limits']['cost']['min'], quote), }, }); const isFrozen = this.safeString (market, 'isFrozen'); const active = (isFrozen !== '1'); const numericId = this.safeInteger (market, 'id'); result.push ({ 'id': id, 'numericId': numericId, 'symbol': symbol, 'baseId': baseId, 'quoteId': quoteId, 'base': base, 'quote': quote, 'active': active, 'limits': limits, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const request = { 'account': 'all', }; const response = await this.privatePostReturnCompleteBalances (this.extend (request, params)); const result = { 'info': response }; const currencyIds = Object.keys (response); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const balance = this.safeValue (response, currencyId, {}); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'onOrders'); result[code] = account; } return this.parseBalance (result); } async fetchTradingFees (params = {}) { await this.loadMarkets (); const fees = await this.privatePostReturnFeeInfo (params); return { 'info': fees, 'maker': this.safeFloat (fees, 'makerFee'), 'taker': this.safeFloat (fees, 'takerFee'), 'withdraw': {}, 'deposit': {}, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'currencyPair': this.marketId (symbol), }; if (limit !== undefined) { request['depth'] = limit; // 100 } const response = await this.publicGetReturnOrderBook (this.extend (request, params)); const orderbook = this.parseOrderBook (response); orderbook['nonce'] = this.safeInteger (response, 'seq'); return orderbook; } async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'currencyPair': 'all', }; if (limit !== undefined) { request['depth'] = limit; // 100 } const response = await this.publicGetReturnOrderBook (this.extend (request, params)); const marketIds = Object.keys (response); const result = {}; for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; let symbol = undefined; if (marketId in this.markets_by_id) { symbol = this.markets_by_id[marketId]['symbol']; } else { const [ quoteId, baseId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } const orderbook = this.parseOrderBook (response[marketId]); orderbook['nonce'] = this.safeInteger (response[marketId], 'seq'); result[symbol] = orderbook; } return result; } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market) { symbol = market['symbol']; } let open = undefined; let change = undefined; let average = undefined; const last = this.safeFloat (ticker, 'last'); const relativeChange = this.safeFloat (ticker, 'percentChange'); if (relativeChange !== -1) { open = last / this.sum (1, relativeChange); change = last - open; average = this.sum (last, open) / 2; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high24hr'), 'low': this.safeFloat (ticker, 'low24hr'), 'bid': this.safeFloat (ticker, 'highestBid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'lowestAsk'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': relativeChange * 100, 'average': average, 'baseVolume': this.safeFloat (ticker, 'quoteVolume'), 'quoteVolume': this.safeFloat (ticker, 'baseVolume'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetReturnTicker (params); const ids = Object.keys (response); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; let symbol = undefined; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } else { const [ quoteId, baseId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; market = { 'symbol': symbol }; } const ticker = response[id]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetReturnCurrencies (params); const ids = Object.keys (response); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const currency = response[id]; const precision = 8; // default precision, todo: fix "magic constants" const code = this.safeCurrencyCode (id); const active = (currency['delisted'] === 0) && !currency['disabled']; const numericId = this.safeInteger (currency, 'id'); result[code] = { 'id': id, 'numericId': numericId, 'code': code, 'info': currency, 'name': currency['name'], 'active': active, 'fee': this.safeFloat (currency, 'txFee'), // todo: redesign 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': currency['txFee'], 'max': Math.pow (10, precision), }, }, }; } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const response = await this.publicGetReturnTicker (params); const ticker = response[market['id']]; return this.parseTicker (ticker, market); } parseTrade (trade, market = undefined) { // // fetchMyTrades (symbol defined, specific market) // // { // globalTradeID: 394698946, // tradeID: 45210255, // date: '2018-10-23 17:28:55', // type: 'sell', // rate: '0.03114126', // amount: '0.00018753', // total: '0.00000583' // } // // fetchMyTrades (symbol undefined, all markets) // // { // globalTradeID: 394131412, // tradeID: '5455033', // date: '2018-10-16 18:05:17', // rate: '0.06935244', // amount: '1.40308443', // total: '0.09730732', // fee: '0.00100000', // orderNumber: '104768235081', // type: 'sell', // category: 'exchange' // } // // createOrder (taker trades) // // { // 'amount': '200.00000000', // 'date': '2019-12-15 16:04:10', // 'rate': '0.00000355', // 'total': '0.00071000', // 'tradeID': '119871', // 'type': 'buy', // 'takerAdjustment': '200.00000000' // } // const id = this.safeString2 (trade, 'globalTradeID', 'tradeID'); const orderId = this.safeString (trade, 'orderNumber'); const timestamp = this.parse8601 (this.safeString (trade, 'date')); let symbol = undefined; let base = undefined; let quote = undefined; if ((!market) && ('currencyPair' in trade)) { const currencyPair = trade['currencyPair']; if (currencyPair in this.markets_by_id) { market = this.markets_by_id[currencyPair]; } else { const parts = currencyPair.split ('_'); quote = parts[0]; base = parts[1]; symbol = base + '/' + quote; } } if (market !== undefined) { symbol = market['symbol']; base = market['base']; quote = market['quote']; } const side = this.safeString (trade, 'type'); let fee = undefined; const price = this.safeFloat (trade, 'rate'); const cost = this.safeFloat (trade, 'total'); const amount = this.safeFloat (trade, 'amount'); if ('fee' in trade) { const rate = this.safeFloat (trade, 'fee'); let feeCost = undefined; let currency = undefined; if (side === 'buy') { currency = base; feeCost = amount * rate; } else { currency = quote; if (cost !== undefined) { feeCost = cost * rate; } } fee = { 'type': undefined, 'rate': rate, 'cost': feeCost, 'currency': currency, }; } let takerOrMaker = undefined; const takerAdjustment = this.safeFloat (trade, 'takerAdjustment'); if (takerAdjustment !== undefined) { takerOrMaker = 'taker'; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': 'limit', 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPair': market['id'], }; if (since !== undefined) { request['start'] = parseInt (since / 1000); request['end'] = this.seconds (); // last 50000 trades by default } const trades = await this.publicGetReturnTradeHistory (this.extend (request, params)); return this.parseTrades (trades, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const pair = market ? market['id'] : 'all'; const request = { 'currencyPair': pair }; if (since !== undefined) { request['start'] = parseInt (since / 1000); request['end'] = this.sum (this.seconds (), 1); // adding 1 is a fix for #3411 } // limit is disabled (does not really work as expected) if (limit !== undefined) { request['limit'] = parseInt (limit); } const response = await this.privatePostReturnTradeHistory (this.extend (request, params)); // // specific market (symbol defined) // // [ // { // globalTradeID: 394700861, // tradeID: 45210354, // date: '2018-10-23 18:01:58', // type: 'buy', // rate: '0.03117266', // amount: '0.00000652', // total: '0.00000020' // }, // { // globalTradeID: 394698946, // tradeID: 45210255, // date: '2018-10-23 17:28:55', // type: 'sell', // rate: '0.03114126', // amount: '0.00018753', // total: '0.00000583' // } // ] // // all markets (symbol undefined) // // { // BTC_BCH: [{ // globalTradeID: 394131412, // tradeID: '5455033', // date: '2018-10-16 18:05:17', // rate: '0.06935244', // amount: '1.40308443', // total: '0.09730732', // fee: '0.00100000', // orderNumber: '104768235081', // type: 'sell', // category: 'exchange' // }, { // globalTradeID: 394126818, // tradeID: '5455007', // date: '2018-10-16 16:55:34', // rate: '0.06935244', // amount: '0.00155709', // total: '0.00010798', // fee: '0.00200000', // orderNumber: '104768179137', // type: 'sell', // category: 'exchange' // }], // } // let result = []; if (market !== undefined) { result = this.parseTrades (response, market); } else { if (response) { const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; const trades = this.parseTrades (response[id], market); for (let j = 0; j < trades.length; j++) { result.push (trades[j]); } } else { const [ quoteId, baseId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const trades = response[id]; for (let j = 0; j < trades.length; j++) { const market = { 'symbol': symbol, 'base': base, 'quote': quote, }; result.push (this.parseTrade (trades[j], market)); } } } } } return this.filterBySinceLimit (result, since, limit); } parseOrderStatus (status) { const statuses = { 'Open': 'open', 'Partially filled': 'open', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // fetchOpenOrder // // { // status: 'Open', // rate: '0.40000000', // amount: '1.00000000', // currencyPair: 'BTC_ETH', // date: '2018-10-17 17:04:50', // total: '0.40000000', // type: 'buy', // startingAmount: '1.00000', // } // // fetchOpenOrders // // { // orderNumber: '514514894224', // type: 'buy', // rate: '0.00001000', // startingAmount: '100.00000000', // amount: '100.00000000', // total: '0.00100000', // date: '2018-10-23 17:38:53', // margin: 0, // } // // createOrder // // { // 'orderNumber': '9805453960', // 'resultingTrades': [ // { // 'amount': '200.00000000', // 'date': '2019-12-15 16:04:10', // 'rate': '0.00000355', // 'total': '0.00071000', // 'tradeID': '119871', // 'type': 'buy', // 'takerAdjustment': '200.00000000', // }, // ], // 'fee': '0.00000000', // 'clientOrderId': '12345', // 'currencyPair': 'BTC_MANA', // // --------------------------------------------------------- // // the following fields are injected by createOrder // 'timestamp': timestamp, // 'status': 'open', // 'type': type, // 'side': side, // 'price': price, // 'amount': amount, // } // let timestamp = this.safeInteger (order, 'timestamp'); if (!timestamp) { timestamp = this.parse8601 (order['date']); } let trades = undefined; if ('resultingTrades' in order) { trades = this.parseTrades (order['resultingTrades'], market); } let symbol = undefined; const marketId = this.safeString (order, 'currencyPair'); market = this.safeValue (this.markets_by_id, marketId, market); if (market !== undefined) { symbol = market['symbol']; } const price = this.safeFloat2 (order, 'price', 'rate'); let remaining = this.safeFloat (order, 'amount'); let amount = this.safeFloat (order, 'startingAmount'); let filled = undefined; let cost = 0; if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; if (price !== undefined) { cost = filled * price; } } } else { amount = remaining; } let status = this.parseOrderStatus (this.safeString (order, 'status')); let average = undefined; let lastTradeTimestamp = undefined; if (filled === undefined) { if (trades !== undefined) { filled = 0; cost = 0; const tradesLength = trades.length; if (tradesLength > 0) { lastTradeTimestamp = trades[0]['timestamp']; for (let i = 0; i < tradesLength; i++) { const trade = trades[i]; const tradeAmount = trade['amount']; const tradePrice = trade['price']; filled = this.sum (filled, tradeAmount); cost = this.sum (cost, tradePrice * tradeAmount); lastTradeTimestamp = Math.max (lastTradeTimestamp, trade['timestamp']); } } remaining = Math.max (amount - filled, 0); if (filled >= amount) { status = 'closed'; } } } if ((filled !== undefined) && (cost !== undefined) && (filled > 0)) { average = cost / filled; } let type = this.safeString (order, 'type'); const side = this.safeString (order, 'side', type); if (type === side) { type = undefined; } const id = this.safeString (order, 'orderNumber'); let fee = undefined; const feeCost = this.safeFloat (order, 'fee'); if (feeCost !== undefined) { let feeCurrencyCode = undefined; if (market !== undefined) { feeCurrencyCode = (side === 'buy') ? market['base'] : market['quote']; } fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } const clientOrderId = this.safeString (order, 'clientOrderId'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'trades': trades, 'fee': fee, }; } parseOpenOrders (orders, market, result) { for (let i = 0; i < orders.length; i++) { const order = orders[i]; const extended = this.extend (order, { 'status': 'open', 'type': 'limit', 'side': order['type'], 'price': order['rate'], }); result.push (this.parseOrder (extended, market)); } return result; } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const pair = market ? market['id'] : 'all'; const request = { 'currencyPair': pair, }; const response = await this.privatePostReturnOpenOrders (this.extend (request, params)); let openOrders = []; if (market !== undefined) { openOrders = this.parseOpenOrders (response, market, openOrders); } else { const marketIds = Object.keys (response); for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const orders = response[marketId]; const m = this.markets_by_id[marketId]; openOrders = this.parseOpenOrders (orders, m, openOrders); } } for (let j = 0; j < openOrders.length; j++) { this.orders[openOrders[j]['id']] = openOrders[j]; } const openOrdersIndexedById = this.indexBy (openOrders, 'id'); const cachedOrderIds = Object.keys (this.orders); const result = []; for (let k = 0; k < cachedOrderIds.length; k++) { const id = cachedOrderIds[k]; if (id in openOrdersIndexedById) { this.orders[id] = this.extend (this.orders[id], openOrdersIndexedById[id]); } else { let order = this.orders[id]; if (order['status'] === 'open') { order = this.extend (order, { 'status': 'closed', 'cost': undefined, 'filled': order['amount'], 'remaining': 0.0, }); if (order['cost'] === undefined) { if (order['filled'] !== undefined) { order['cost'] = order['filled'] * order['price']; } } this.orders[id] = order; } } const order = this.orders[id]; if (market !== undefined) { if (order['symbol'] === symbol) { result.push (order); } } else { result.push (order); } } return this.filterBySinceLimit (result, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { const since = this.safeValue (params, 'since'); const limit = this.safeValue (params, 'limit'); const request = this.omit (params, [ 'since', 'limit' ]); const orders = await this.fetchOrders (symbol, since, limit, request); for (let i = 0; i < orders.length; i++) { if (orders[i]['id'] === id) { return orders[i]; } } throw new OrderNotCached (this.id + ' order id ' + id.toString () + ' is not in "open" state and not found in cache'); } filterOrdersByStatus (orders, status) { const result = []; for (let i = 0; i < orders.length; i++) { if (orders[i]['status'] === status) { result.push (orders[i]); } } return result; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterOrdersByStatus (orders, 'open'); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterOrdersByStatus (orders, 'closed'); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type !== 'limit') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const method = 'privatePost' + this.capitalize (side); const market = this.market (symbol); amount = this.amountToPrecision (symbol, amount); const request = { 'currencyPair': market['id'], 'rate': this.priceToPrecision (symbol, price), 'amount': amount, }; // remember the timestamp before issuing the request const timestamp = this.milliseconds (); const response = await this[method] (this.extend (request, params)); // // { // 'orderNumber': '9805453960', // 'resultingTrades': [ // { // 'amount': '200.00000000', // 'date': '2019-12-15 16:04:10', // 'rate': '0.00000355', // 'total': '0.00071000', // 'tradeID': '119871', // 'type': 'buy', // 'takerAdjustment': '200.00000000', // }, // ], // 'fee': '0.00000000', // 'currencyPair': 'BTC_MANA', // } // const order = this.parseOrder (this.extend ({ 'timestamp': timestamp, 'status': 'open', 'type': type, 'side': side, 'price': price, 'amount': amount, }, response), market); const id = order['id']; this.orders[id] = order; return this.extend ({ 'info': response }, order); } async editOrder (id, symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); price = parseFloat (price); const request = { 'orderNumber': id, 'rate': this.priceToPrecision (symbol, price), }; if (amount !== undefined) { request['amount'] = this.amountToPrecision (symbol, amount); } const response = await this.privatePostMoveOrder (this.extend (request, params)); let result = undefined; if (id in this.orders) { this.orders[id]['status'] = 'canceled'; const newid = response['orderNumber']; this.orders[newid] = this.extend (this.orders[id], { 'id': newid, 'price': price, 'status': 'open', }); if (amount !== undefined) { this.orders[newid]['amount'] = amount; } result = this.extend (this.orders[newid], { 'info': response }); } else { let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } result = this.parseOrder (response, market); this.orders[result['id']] = result; } return result; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); let response = undefined; try { response = await this.privatePostCancelOrder (this.extend ({ 'orderNumber': id, }, params)); } catch (e) { if (e instanceof CancelPending) { // A request to cancel the order has been sent already. // If we then attempt to cancel the order the second time // before the first request is processed the exchange will // throw a CancelPending exception. Poloniex won't show the // order in the list of active (open) orders and the cached // order will be marked as 'closed' (see #1801 for details). // To avoid that we proactively mark the order as 'canceled' // here. If for some reason the order does not get canceled // and still appears in the active list then the order cache // will eventually get back in sync on a call to `fetchOrder`. if (id in this.orders) { this.orders[id]['status'] = 'canceled'; } } throw e; } if (id in this.orders) { this.orders[id]['status'] = 'canceled'; } return response; } async cancelAllOrders (symbol = undefined, params = {}) { const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['currencyPair'] = market['id']; } const response = await this.privatePostCancelAllOrders (this.extend (request, params)); // // { // "success": 1, // "message": "Orders canceled", // "orderNumbers": [ // 503749, // 888321, // 7315825, // 7316824 // ] // } // const orderIds = this.safeValue (response, 'orderNumbers', []); for (let i = 0; i < orderIds.length; i++) { const id = orderIds[i].toString (); if (id in this.orders) { this.orders[id]['status'] = 'canceled'; } } return response; } async fetchOpenOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); id = id.toString (); const response = await this.privatePostReturnOrderStatus (this.extend ({ 'orderNumber': id, }, params)); // // { // success: 1, // result: { // '6071071': { // status: 'Open', // rate: '0.40000000', // amount: '1.00000000', // currencyPair: 'BTC_ETH', // date: '2018-10-17 17:04:50', // total: '0.40000000', // type: 'buy', // startingAmount: '1.00000', // }, // }, // } // const result = this.safeValue (response['result'], id); if (result === undefined) { throw new OrderNotFound (this.id + ' order id ' + id + ' not found'); } const order = this.parseOrder (result); order['id'] = id; this.orders[id] = order; return order; } async fetchOrderStatus (id, symbol = undefined, params = {}) { await this.loadMarkets (); const orders = await this.fetchOpenOrders (symbol, undefined, undefined, params); const indexed = this.indexBy (orders, 'id'); return (id in indexed) ? 'open' : 'closed'; } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderNumber': id, }; const trades = await this.privatePostReturnOrderTrades (this.extend (request, params)); return this.parseTrades (trades); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privatePostGenerateNewAddress (this.extend (request, params)); let address = undefined; let tag = undefined; if (response['success'] === 1) { address = this.safeString (response, 'response'); } this.checkAddress (address); const depositAddress = this.safeString (currency['info'], 'depositAddress'); if (depositAddress !== undefined) { tag = address; address = depositAddress; } return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const response = await this.privatePostReturnDepositAddresses (params); const currencyId = currency['id']; let address = this.safeString (response, currencyId); let tag = undefined; this.checkAddress (address); const depositAddress = this.safeString (currency['info'], 'depositAddress'); if (depositAddress !== undefined) { tag = address; address = depositAddress; } return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'amount': amount, 'address': address, }; if (tag) { request['paymentId'] = tag; } const response = await this.privatePostWithdraw (this.extend (request, params)); return { 'info': response, 'id': this.safeString (response, 'withdrawalNumber'), }; } async fetchTransactionsHelper (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const year = 31104000; // 60 * 60 * 24 * 30 * 12 = one year of history, why not const now = this.seconds (); const start = (since !== undefined) ? parseInt (since / 1000) : now - 10 * year; const request = { 'start': start, // UNIX timestamp, required 'end': now, // UNIX timestamp, required }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privatePostReturnDepositsWithdrawals (this.extend (request, params)); // // { deposits: [ { currency: "BTC", // address: "1MEtiqJWru53FhhHrfJPPvd2tC3TPDVcmW", // amount: "0.01063000", // confirmations: 1, // txid: "952b0e1888d6d491591facc0d37b5ebec540ac1efb241fdbc22bcc20d1822fb6", // timestamp: 1507916888, // status: "COMPLETE" }, // { currency: "ETH", // address: "0x20108ba20b65c04d82909e91df06618107460197", // amount: "4.00000000", // confirmations: 38, // txid: "0x4be260073491fe63935e9e0da42bd71138fdeb803732f41501015a2d46eb479d", // timestamp: 1525060430, // status: "COMPLETE" } ], // withdrawals: [ { withdrawalNumber: 8224394, // currency: "EMC2", // address: "EYEKyCrqTNmVCpdDV8w49XvSKRP9N3EUyF", // amount: "63.10796020", // fee: "0.01000000", // timestamp: 1510819838, // status: "COMPLETE: d37354f9d02cb24d98c8c4fc17aa42f475530b5727effdf668ee5a43ce667fd6", // ipAddress: "5.220.220.200" }, // { withdrawalNumber: 9290444, // currency: "ETH", // address: "0x191015ff2e75261d50433fbd05bd57e942336149", // amount: "0.15500000", // fee: "0.00500000", // timestamp: 1514099289, // status: "COMPLETE: 0x12d444493b4bca668992021fd9e54b5292b8e71d9927af1f076f554e4bea5b2d", // ipAddress: "5.228.227.214" }, // { withdrawalNumber: 11518260, // currency: "BTC", // address: "8JoDXAmE1GY2LRK8jD1gmAmgRPq54kXJ4t", // amount: "0.20000000", // fee: "0.00050000", // timestamp: 1527918155, // status: "COMPLETE: 1864f4ebb277d90b0b1ff53259b36b97fa1990edc7ad2be47c5e0ab41916b5ff", // ipAddress: "211.8.195.26" } ] } // return response; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.fetchTransactionsHelper (code, since, limit, params); for (let i = 0; i < response['deposits'].length; i++) { response['deposits'][i]['type'] = 'deposit'; } for (let i = 0; i < response['withdrawals'].length; i++) { response['withdrawals'][i]['type'] = 'withdrawal'; } let currency = undefined; if (code !== undefined) { currency = this.currency (code); } const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); const deposits = this.parseTransactions (response['deposits'], currency, since, limit); const transactions = this.arrayConcat (deposits, withdrawals); return this.filterByCurrencySinceLimit (this.sortBy (transactions, 'timestamp'), code, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { const response = await this.fetchTransactionsHelper (code, since, limit, params); for (let i = 0; i < response['withdrawals'].length; i++) { response['withdrawals'][i]['type'] = 'withdrawal'; } let currency = undefined; if (code !== undefined) { currency = this.currency (code); } const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); return this.filterByCurrencySinceLimit (withdrawals, code, since, limit); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { const response = await this.fetchTransactionsHelper (code, since, limit, params); for (let i = 0; i < response['deposits'].length; i++) { response['deposits'][i]['type'] = 'deposit'; } let currency = undefined; if (code !== undefined) { currency = this.currency (code); } const deposits = this.parseTransactions (response['deposits'], currency, since, limit); return this.filterByCurrencySinceLimit (deposits, code, since, limit); } parseTransactionStatus (status) { const statuses = { 'COMPLETE': 'ok', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // deposits // // { // "txid": "f49d489616911db44b740612d19464521179c76ebe9021af85b6de1e2f8d68cd", // "type": "deposit", // "amount": "49798.01987021", // "status": "COMPLETE", // "address": "DJVJZ58tJC8UeUv9Tqcdtn6uhWobouxFLT", // "currency": "DOGE", // "timestamp": 1524321838, // "confirmations": 3371, // "depositNumber": 134587098 // } // // withdrawals // // { // "fee": "0.00050000", // "type": "withdrawal", // "amount": "0.40234387", // "status": "COMPLETE: fbabb2bf7d81c076f396f3441166d5f60f6cea5fdfe69e02adcc3b27af8c2746", // "address": "1EdAqY4cqHoJGAgNfUFER7yZpg1Jc9DUa3", // "currency": "BTC", // "canCancel": 0, // "ipAddress": "185.230.101.31", // "paymentID": null, // "timestamp": 1523834337, // "canResendEmail": 0, // "withdrawalNumber": 11162900 // } // const timestamp = this.safeTimestamp (transaction, 'timestamp'); const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId); let status = this.safeString (transaction, 'status', 'pending'); let txid = this.safeString (transaction, 'txid'); if (status !== undefined) { const parts = status.split (': '); const numParts = parts.length; status = parts[0]; if ((numParts > 1) && (txid === undefined)) { txid = parts[1]; } status = this.parseTransactionStatus (status); } const type = this.safeString (transaction, 'type'); const id = this.safeString2 (transaction, 'withdrawalNumber', 'depositNumber'); let amount = this.safeFloat (transaction, 'amount'); const address = this.safeString (transaction, 'address'); let feeCost = this.safeFloat (transaction, 'fee'); if (feeCost === undefined) { // according to https://poloniex.com/fees/ feeCost = 0; // FIXME: remove hardcoded value that may change any time } if (type === 'withdrawal') { // poloniex withdrawal amount includes the fee amount = amount - feeCost; } return { 'info': transaction, 'id': id, 'currency': code, 'amount': amount, 'address': address, 'tag': undefined, 'status': status, 'type': type, 'updated': undefined, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': { 'currency': code, 'cost': feeCost, }, }; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; const query = this.extend ({ 'command': path }, params); if (api === 'public') { url += '?' + this.urlencode (query); } else { this.checkRequiredCredentials (); query['nonce'] = this.nonce (); body = this.urlencode (query); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } // {"error":"Permission denied."} if ('error' in response) { const message = response['error']; const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],117:[function(require,module,exports){ 'use strict'; const Exchange = require ('./base/Exchange'); const { ROUND } = require ('./base/functions/number'); const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder, OrderNotFound } = require ('./base/errors'); module.exports = class rightbtc extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'rightbtc', 'name': 'RightBTC', 'countries': [ 'AE' ], 'has': { 'privateAPI': false, 'fetchTickers': true, 'fetchOHLCV': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': false, 'fetchOrder': 'emulated', 'fetchMyTrades': true, }, 'timeframes': { '1m': 'min1', '5m': 'min5', '15m': 'min15', '30m': 'min30', '1h': 'hr1', '1d': 'day1', '1w': 'week', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/42633917-7d20757e-85ea-11e8-9f53-fffe9fbb7695.jpg', 'api': 'https://www.rightbtc.com/api', 'www': 'https://www.rightbtc.com', 'doc': [ 'https://docs.rightbtc.com/api/', ], // eslint-disable-next-line no-useless-escape // 'fees': 'https://www.rightbtc.com/\#\!/support/fee', }, 'api': { 'public': { 'get': [ // 'getAssetsTradingPairs/zh', // 404 'trading_pairs', 'ticker/{trading_pair}', 'tickers', 'depth/{trading_pair}', 'depth/{trading_pair}/{count}', 'trades/{trading_pair}', 'trades/{trading_pair}/{count}', 'candlestick/latest/{trading_pair}', 'candlestick/{timeSymbol}/{trading_pair}', 'candlestick/{timeSymbol}/{trading_pair}/{count}', ], }, 'trader': { 'get': [ 'balance/{symbol}', 'balances', 'deposits/{asset}/{page}', 'withdrawals/{asset}/{page}', 'orderpage/{trading_pair}/{cursor}', 'orders/{trading_pair}/{ids}', // ids are a slash-separated list of {id}/{id}/{id}/... 'history/{trading_pair}/{ids}', 'historys/{trading_pair}/{page}', 'trading_pairs', ], 'post': [ 'order', ], 'delete': [ 'order/{trading_pair}/{ids}', ], }, }, // HARDCODING IS DEPRECATED, THE FEES BELOW SHOULD BE REWRITTEN 'fees': { 'trading': { // min trading fees // 0.0001 BTC // 0.01 ETP // 0.001 ETH // 0.1 BITCNY 'maker': 0.1 / 100, 'taker': 0.2 / 100, }, 'funding': { 'withdraw': { // 'BTM': n => 3 + n * (1 / 100), // 'ZDC': n => 1 + n * (0.5 / 100), // 'ZGC': n => 0.5 + n * (0.5 / 100), // 'BTS': n => 1 + n * (1 / 100), // 'DLT': n => 3 + n * (1 / 100), // 'SNT': n => 10 + n * (1 / 100), // 'XNC': n => 1 + n * (1 / 100), // 'ICO': n => 3 + n * (1 / 100), // 'CMC': n => 1 + n * (0.5 / 100), // 'GXS': n => 0.2 + n * (1 / 100), // 'OBITS': n => 0.3 + n * (1 / 100), // 'ICS': n => 2 + n * (1 / 100), // 'TIC': n => 2 + n * (1 / 100), // 'IND': n => 20 + n * (1 / 100), // 'MVC': n => 20 + n * (1 / 100), // 'BitCNY': n => 0.1 + n * (1 / 100), // 'MTX': n => 1 + n * (1 / 100), 'ETP': 0.01, 'BTC': 0.0005, 'ETH': 0.005, 'ETC': 0.01, 'STORJ': 3, 'LTC': 0.01, 'ZEC': 0.001, 'BCC': 0.001, 'XRB': 0, 'NXS': 0.1, }, }, }, 'commonCurrencies': { 'XRB': 'NANO', }, 'exceptions': { 'ERR_USERTOKEN_NOT_FOUND': AuthenticationError, 'ERR_ASSET_NOT_EXISTS': ExchangeError, 'ERR_ASSET_NOT_AVAILABLE': ExchangeError, 'ERR_BALANCE_NOT_ENOUGH': InsufficientFunds, 'ERR_CREATE_ORDER': InvalidOrder, 'ERR_CANDLESTICK_DATA': ExchangeError, }, }); } async fetchMarkets (params = {}) { // let zh = await this.publicGetGetAssetsTradingPairsZh (); const markets = await this.publicGetTradingPairs (params); const marketIds = Object.keys (markets); const result = []; for (let i = 0; i < marketIds.length; i++) { const id = marketIds[i]; const market = markets[id]; const baseId = this.safeString (market, 'bid_asset_symbol'); const quoteId = this.safeString (market, 'ask_asset_symbol'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'bid_asset_decimals'), 'price': this.safeInteger (market, 'ask_asset_decimals'), }; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['price']), }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': Math.pow (10, precision['price']), }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }); } return result; } divideSafeFloat (x, key, divisor) { const value = this.safeFloat (x, key); if (value !== undefined) { return value / divisor; } return value; } parseTicker (ticker, market = undefined) { const symbol = market['symbol']; const timestamp = this.safeInteger (ticker, 'date'); const last = this.divideSafeFloat (ticker, 'last', 1e8); const high = this.divideSafeFloat (ticker, 'high', 1e8); const low = this.divideSafeFloat (ticker, 'low', 1e8); const bid = this.divideSafeFloat (ticker, 'buy', 1e8); const ask = this.divideSafeFloat (ticker, 'sell', 1e8); const baseVolume = this.divideSafeFloat (ticker, 'vol24h', 1e8); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': high, 'low': low, 'bid': bid, 'bidVolume': undefined, 'ask': ask, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': undefined, 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'trading_pair': market['id'], }; const response = await this.publicGetTickerTradingPair (this.extend (request, params)); const result = this.safeValue (response, 'result'); if (result === undefined) { throw new ExchangeError (this.id + ' fetchTicker returned an empty response for symbol ' + symbol); } return this.parseTicker (result, market); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTickers (params); const tickers = response['result']; const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = tickers[i]; const id = ticker['market']; if (!(id in this.marketsById)) { continue; } const market = this.marketsById[id]; const symbol = market['symbol']; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'trading_pair': this.marketId (symbol), }; let method = 'publicGetDepthTradingPair'; if (limit !== undefined) { method += 'Count'; request['count'] = limit; } const response = await this[method] (this.extend (request, params)); const bidsasks = {}; const types = ['bid', 'ask']; for (let ti = 0; ti < types.length; ti++) { const type = types[ti]; bidsasks[type] = []; for (let i = 0; i < response['result'][type].length; i++) { const [ price, amount, total ] = response['result'][type][i]; bidsasks[type].push ([ price / 1e8, amount / 1e8, total / 1e8, ]); } } return this.parseOrderBook (bidsasks, undefined, 'bid', 'ask'); } parseTrade (trade, market = undefined) { // // { // "order_id": 118735, // "trade_id": 7, // "trading_pair": "BTCCNY", // "side": "B", // "quantity": 1000000000, // "price": 900000000, // "created_at": "2017-06-06T20:45:27.000Z" // } // let timestamp = this.safeInteger (trade, 'date'); if (timestamp === undefined) { timestamp = this.parse8601 (this.safeString (trade, 'created_at')); } let id = this.safeString (trade, 'tid'); id = this.safeString (trade, 'trade_id', id); const orderId = this.safeString (trade, 'order_id'); const price = this.divideSafeFloat (trade, 'price', 1e8); let amount = this.safeFloat (trade, 'amount'); amount = this.safeFloat (trade, 'quantity', amount); if (amount !== undefined) { amount = amount / 1e8; } let symbol = undefined; if (market === undefined) { const marketId = this.safeString (trade, 'trading_pair'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } let cost = this.costToPrecision (symbol, price * amount); cost = parseFloat (cost); let side = this.safeStringLower (trade, 'side'); if (side === 'b') { side = 'buy'; } else if (side === 's') { side = 'sell'; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': 'limit', 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'trading_pair': market['id'], }; const response = await this.publicGetTradesTradingPair (this.extend (request, params)); return this.parseTrades (response['result'], market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { return [ parseInt (ohlcv[0]), parseFloat (ohlcv[2]) / 1e8, parseFloat (ohlcv[3]) / 1e8, parseFloat (ohlcv[4]) / 1e8, parseFloat (ohlcv[5]) / 1e8, parseFloat (ohlcv[1]) / 1e8, ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'trading_pair': market['id'], 'timeSymbol': this.timeframes[timeframe], }; const response = await this.publicGetCandlestickTimeSymbolTradingPair (this.extend (request, params)); return this.parseOHLCVs (response['result'], market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.traderGetBalances (params); // // { // "status": { // "success": 1, // "message": "GET_BALANCES" // }, // "result": [ // { // "asset": "ETP", // "balance": "5000000000000", // "frozen": "0", // "state": "1" // }, // { // "asset": "CNY", // "balance": "10000000000000", // "frozen": "240790000", // "state": "1" // } // ] // } // const result = { 'info': response }; const balances = this.safeValue (response, 'result', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'asset'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); // https://github.com/ccxt/ccxt/issues/3873 account['free'] = this.divideSafeFloat (balance, 'balance', 1e8); account['used'] = this.divideSafeFloat (balance, 'frozen', 1e8); result[code] = account; } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const order = { 'trading_pair': market['id'], // We need to use decimalToPrecision here, since // 0.036*1e8 === 3599999.9999999995 // which would get truncated to 3599999 after parseInt // which would then be rejected by rightBtc because it's too precise 'quantity': parseInt (this.decimalToPrecision (amount * 1e8, ROUND, 0, this.precisionMode)), 'limit': parseInt (this.decimalToPrecision (price * 1e8, ROUND, 0, this.precisionMode)), 'type': type.toUpperCase (), 'side': side.toUpperCase (), }; const response = await this.traderPostOrder (this.extend (order, params)); return this.parseOrder (response); } async cancelOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'trading_pair': market['id'], 'ids': id, }; const response = await this.traderDeleteOrderTradingPairIds (this.extend (request, params)); return response; } parseOrderStatus (status) { const statuses = { 'NEW': 'open', 'TRADE': 'closed', // TRADE means filled or partially filled orders 'CANCEL': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // fetchOrder / fetchOpenOrders // // { // "id": 4180528, // "quantity": 20000000, // "rest": 20000000, // "limit": 1000000, // "price": null, // "side": "BUY", // "created": 1496005693738 // } // // fetchOrders // // { // "trading_pair": "ETPCNY", // "status": "TRADE", // "fee": 0.23, // "min_fee": 10000000, // "created_at": "2017-05-25T00:12:27.000Z", // "cost": 1152468000000, // "limit": 3600000000, // "id": 11060, // "quantity": 32013000000, // "filled_quantity": 32013000000 // } // const id = this.safeString (order, 'id'); const status = this.parseOrderStatus (this.safeString (order, 'status')); const marketId = this.safeString (order, 'trading_pair'); if (market === undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } let symbol = marketId; if (market !== undefined) { symbol = market['symbol']; } let timestamp = this.safeInteger (order, 'created'); if (timestamp === undefined) { timestamp = this.parse8601 (this.safeString (order, 'created_at')); } if ('time' in order) { timestamp = order['time']; } else if ('transactTime' in order) { timestamp = order['transactTime']; } let price = this.safeFloat2 (order, 'limit', 'price'); if (price !== undefined) { price = price / 1e8; } const amount = this.divideSafeFloat (order, 'quantity', 1e8); let filled = this.divideSafeFloat (order, 'filled_quantity', 1e8); let remaining = this.divideSafeFloat (order, 'rest', 1e8); const cost = this.divideSafeFloat (order, 'cost', 1e8); // lines 483-494 should be generalized into a base class method if (amount !== undefined) { if (remaining === undefined) { if (filled !== undefined) { remaining = Math.max (0, amount - filled); } } if (filled === undefined) { if (remaining !== undefined) { filled = Math.max (0, amount - remaining); } } } const type = 'limit'; const side = this.safeStringLower (order, 'side'); const feeCost = this.divideSafeFloat (order, 'min_fee', 1e8); let fee = undefined; if (feeCost !== undefined) { let feeCurrency = undefined; if (market !== undefined) { feeCurrency = market['quote']; } fee = { 'rate': this.safeFloat (order, 'fee'), 'cost': feeCost, 'currency': feeCurrency, }; } const trades = undefined; return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': trades, 'average': undefined, }; } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'trading_pair': market['id'], 'ids': id, }; const response = await this.traderGetOrdersTradingPairIds (this.extend (request, params)); // // let response = { // "status": { // "success": 1, // "message": "SUC_LIST_AVTICE_ORDERS" // }, // "result": [ // { // "id": 4180528, // "quantity": 20000000, // "rest": 20000000, // "limit": 1000000, // "price": null, // "side": "BUY", // "created": 1496005693738 // } // ] // } // const orders = this.parseOrders (response['result'], market); const ordersById = this.indexBy (orders, 'id'); if (!(id in ordersById)) { throw new OrderNotFound (this.id + ' fetchOrder could not find order ' + id.toString () + ' in open orders.'); } return ordersById[id]; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'trading_pair': market['id'], 'cursor': 0, }; const response = await this.traderGetOrderpageTradingPairCursor (this.extend (request, params)); // // let response = { // "status": { // "success": 1, // "message": "SUC_LIST_AVTICE_ORDERS_PAGE" // }, // "result": { // "cursor": "0", // "orders": [ // { // "id": 4180528, // "quantity": 20000000, // "rest": 20000000, // "limit": 1000000, // "price": null, // "side": "BUY", // "created": 1496005693738 // } // ] // } // } // return this.parseOrders (response['result']['orders'], market, since, limit); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { let ids = this.safeString (params, 'ids'); if ((symbol === undefined) || (ids === undefined)) { throw new ArgumentsRequired (this.id + " fetchOrders requires a 'symbol' argument and an extra 'ids' parameter. The 'ids' should be an array or a string of one or more order ids separated with slashes."); // eslint-disable-line quotes } if (Array.isArray (ids)) { ids = ids.join ('/'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'trading_pair': market['id'], 'ids': ids, }; const response = await this.traderGetHistoryTradingPairIds (this.extend (request, params)); // // let response = { // "status": { // "success": 1, // "message": null // }, // "result": [ // { // "trading_pair": "ETPCNY", // "status": "TRADE", // "fee": 0.23, // "min_fee": 10000000, // "created_at": "2017-05-25T00:12:27.000Z", // "cost": 1152468000000, // "limit": 3600000000, // "id": 11060, // "quantity": 32013000000, // "filled_quantity": 32013000000 // } // ] // } // return this.parseOrders (response['result'], undefined, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'trading_pair': market['id'], 'page': 0, }; const response = await this.traderGetHistorysTradingPairPage (this.extend (request, params)); // // let response = { // "status": { // "success": 1, // "message": null // }, // "result": [ // { // "order_id": 118735, // "trade_id": 7, // "trading_pair": "BTCCNY", // "side": "B", // "quantity": 1000000000, // "price": 900000000, // "created_at": "2017-06-06T20:45:27.000Z" // }, // { // "order_id": 118734, // "trade_id": 7, // "trading_pair": "BTCCNY", // "side": "S", // "quantity": 1000000000, // "price": 900000000, // "created_at": "2017-06-06T20:45:27.000Z" // } // ] // } // return this.parseTrades (response['result'], undefined, since, limit); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'] + '/' + api + '/' + this.implodeParams (path, params); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); headers = { 'apikey': this.apiKey, 'signature': this.secret, }; if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { body = this.json (query); headers['Content-Type'] = 'application/json'; } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } const status = this.safeValue (response, 'status'); if (status !== undefined) { // // {"status":{"success":0,"message":"ERR_USERTOKEN_NOT_FOUND"}} // const success = this.safeString (status, 'success'); if (success !== '1') { const message = this.safeString (status, 'message'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions, message, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],118:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); // --------------------------------------------------------------------------- module.exports = class southxchange extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'southxchange', 'name': 'SouthXchange', 'countries': [ 'AR' ], // Argentina 'rateLimit': 1000, 'has': { 'CORS': true, 'createDepositAddress': true, 'fetchOpenOrders': true, 'fetchTickers': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27838912-4f94ec8a-60f6-11e7-9e5d-bbf9bd50a559.jpg', 'api': 'https://www.southxchange.com/api', 'www': 'https://www.southxchange.com', 'doc': 'https://www.southxchange.com/Home/Api', }, 'api': { 'public': { 'get': [ 'markets', 'price/{symbol}', 'prices', 'book/{symbol}', 'trades/{symbol}', ], }, 'private': { 'post': [ 'cancelMarketOrders', 'cancelOrder', 'getOrder', 'generatenewaddress', 'listOrders', 'listBalances', 'listTransactions', 'placeOrder', 'withdraw', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.1 / 100, 'taker': 0.3 / 100, }, }, 'commonCurrencies': { 'SMT': 'SmartNode', 'MTC': 'Marinecoin', 'BHD': 'Bithold', }, }); } async fetchMarkets (params = {}) { const markets = await this.publicGetMarkets (params); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const baseId = market[0]; const quoteId = market[1]; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const id = baseId + '/' + quoteId; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': undefined, 'info': market, 'precision': this.precision, 'limits': this.limits, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostListBalances (params); const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'Currency'); const code = this.safeCurrencyCode (currencyId); const deposited = this.safeFloat (balance, 'Deposited'); const unconfirmed = this.safeFloat (balance, 'Unconfirmed'); const account = this.account (); account['free'] = this.safeFloat (balance, 'Available'); account['total'] = this.sum (deposited, unconfirmed); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'symbol': this.marketId (symbol), }; const response = await this.publicGetBookSymbol (this.extend (request, params)); return this.parseOrderBook (response, undefined, 'BuyOrders', 'SellOrders', 'Price', 'Amount'); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'Last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'Bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'Ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': this.safeFloat (ticker, 'Variation24Hr'), 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'Volume24Hr'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetPrices (params); const tickers = this.indexBy (response, 'Market'); const ids = Object.keys (tickers); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; let symbol = id; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } const ticker = tickers[id]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetPriceSymbol (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market) { const timestamp = this.safeTimestamp (trade, 'At'); const price = this.safeFloat (trade, 'Price'); const amount = this.safeFloat (trade, 'Amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } const side = this.safeString (trade, 'Type'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': undefined, 'order': undefined, 'type': undefined, 'side': side, 'price': price, 'takerOrMaker': undefined, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetTradesSymbol (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseOrder (order, market = undefined) { const status = 'open'; const baseId = this.safeString (order, 'ListingCurrency'); const quoteId = this.safeString (order, 'ReferenceCurrency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const timestamp = undefined; const price = this.safeFloat (order, 'LimitPrice'); const amount = this.safeFloat (order, 'OriginalAmount'); const remaining = this.safeFloat (order, 'Amount'); let filled = undefined; let cost = undefined; if (amount !== undefined) { cost = price * amount; if (remaining !== undefined) { filled = amount - remaining; } } const type = 'limit'; const side = this.safeStringLower (order, 'Type'); const id = this.safeString (order, 'Code'); const result = { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'average': undefined, 'trades': undefined, }; return result; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const response = await this.privatePostListOrders (params); return this.parseOrders (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'listingCurrency': market['base'], 'referenceCurrency': market['quote'], 'type': side, 'amount': amount, }; if (type === 'limit') { request['limitPrice'] = price; } const response = await this.privatePostPlaceOrder (this.extend (request, params)); return { 'info': response, 'id': response.toString (), }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderCode': id, }; return await this.privatePostCancelOrder (this.extend (request, params)); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privatePostGeneratenewaddress (this.extend (request, params)); const parts = response.split ('|'); const numParts = parts.length; const address = parts[0]; this.checkAddress (address); let tag = undefined; if (numParts > 1) { tag = parts[1]; } return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], 'address': address, 'amount': amount, }; if (tag !== undefined) { request['address'] = address + '|' + tag; } const response = await this.privatePostWithdraw (this.extend (request, params)); return { 'info': response, 'id': undefined, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const url = this.urls['api'] + '/' + this.implodeParams (path, params); let query = this.omit (params, this.extractParams (path)); if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce (); query = this.extend ({ 'key': this.apiKey, 'nonce': nonce, }, query); body = this.json (query); headers = { 'Content-Type': 'application/json', 'Hash': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7}],119:[function(require,module,exports){ (function (module, exports) { 'use strict'; // Utils function assert (val, msg) { if (!val) throw new Error(msg || 'Assertion failed'); } // Could use `inherits` module, but don't want to move from single file // architecture yet. function inherits (ctor, superCtor) { ctor.super_ = superCtor; var TempCtor = function () {}; TempCtor.prototype = superCtor.prototype; ctor.prototype = new TempCtor(); ctor.prototype.constructor = ctor; } // BN function BN (number, base, endian) { if (BN.isBN(number)) { return number; } this.negative = 0; this.words = null; this.length = 0; // Reduction context this.red = null; if (number !== null) { if (base === 'le' || base === 'be') { endian = base; base = 10; } this._init(number || 0, base || 10, endian || 'be'); } } if (typeof module === 'object') { module.exports = BN; } else { exports.BN = BN; } BN.BN = BN; BN.wordSize = 26; var Buffer; try { Buffer = require('buffer').Buffer; } catch (e) { } BN.isBN = function isBN (num) { if (num instanceof BN) { return true; } return num !== null && typeof num === 'object' && num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); }; BN.max = function max (left, right) { if (left.cmp(right) > 0) return left; return right; }; BN.min = function min (left, right) { if (left.cmp(right) < 0) return left; return right; }; BN.prototype._init = function init (number, base, endian) { if (typeof number === 'number') { return this._initNumber(number, base, endian); } if (typeof number === 'object') { return this._initArray(number, base, endian); } if (base === 'hex') { base = 16; } assert(base === (base | 0) && base >= 2 && base <= 36); number = number.toString().replace(/\s+/g, ''); var start = 0; if (number[0] === '-') { start++; } if (base === 16) { this._parseHex(number, start); } else { this._parseBase(number, base, start); } if (number[0] === '-') { this.negative = 1; } this._strip(); if (endian !== 'le') return; this._initArray(this.toArray(), base, endian); }; BN.prototype._initNumber = function _initNumber (number, base, endian) { if (number < 0) { this.negative = 1; number = -number; } if (number < 0x4000000) { this.words = [number & 0x3ffffff]; this.length = 1; } else if (number < 0x10000000000000) { this.words = [ number & 0x3ffffff, (number / 0x4000000) & 0x3ffffff ]; this.length = 2; } else { assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) this.words = [ number & 0x3ffffff, (number / 0x4000000) & 0x3ffffff, 1 ]; this.length = 3; } if (endian !== 'le') return; // Reverse the bytes this._initArray(this.toArray(), base, endian); }; BN.prototype._initArray = function _initArray (number, base, endian) { // Perhaps a Uint8Array assert(typeof number.length === 'number'); if (number.length <= 0) { this.words = [0]; this.length = 1; return this; } this.length = Math.ceil(number.length / 3); this.words = new Array(this.length); for (var i = 0; i < this.length; i++) { this.words[i] = 0; } var j, w; var off = 0; if (endian === 'be') { for (i = number.length - 1, j = 0; i >= 0; i -= 3) { w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); this.words[j] |= (w << off) & 0x3ffffff; this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; off += 24; if (off >= 26) { off -= 26; j++; } } } else if (endian === 'le') { for (i = 0, j = 0; i < number.length; i += 3) { w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); this.words[j] |= (w << off) & 0x3ffffff; this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; off += 24; if (off >= 26) { off -= 26; j++; } } } return this._strip(); }; function parseHex (str, start, end) { var r = 0; var len = Math.min(str.length, end); var z = 0; for (var i = start; i < len; i++) { var c = str.charCodeAt(i) - 48; r <<= 4; var b; // 'a' - 'f' if (c >= 49 && c <= 54) { b = c - 49 + 0xa; // 'A' - 'F' } else if (c >= 17 && c <= 22) { b = c - 17 + 0xa; // '0' - '9' } else { b = c; } r |= b; z |= b; } assert(!(z & 0xf0), 'Invalid character in ' + str); return r; } BN.prototype._parseHex = function _parseHex (number, start) { // Create possibly bigger array to ensure that it fits the number this.length = Math.ceil((number.length - start) / 6); this.words = new Array(this.length); for (var i = 0; i < this.length; i++) { this.words[i] = 0; } var j, w; // Scan 24-bit chunks and add them to the number var off = 0; for (i = number.length - 6, j = 0; i >= start; i -= 6) { w = parseHex(number, i, i + 6); this.words[j] |= (w << off) & 0x3ffffff; // NOTE: `0x3fffff` is intentional here, 26bits max shift + 24bit hex limb this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; off += 24; if (off >= 26) { off -= 26; j++; } } if (i + 6 !== start) { w = parseHex(number, start, i + 6); this.words[j] |= (w << off) & 0x3ffffff; this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; } this._strip(); }; function parseBase (str, start, end, mul) { var r = 0; var b = 0; var len = Math.min(str.length, end); for (var i = start; i < len; i++) { var c = str.charCodeAt(i) - 48; r *= mul; // 'a' if (c >= 49) { b = c - 49 + 0xa; // 'A' } else if (c >= 17) { b = c - 17 + 0xa; // '0' - '9' } else { b = c; } assert(c >= 0 && b < mul, 'Invalid character'); r += b; } return r; } BN.prototype._parseBase = function _parseBase (number, base, start) { // Initialize as zero this.words = [0]; this.length = 1; // Find length of limb in base for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { limbLen++; } limbLen--; limbPow = (limbPow / base) | 0; var total = number.length - start; var mod = total % limbLen; var end = Math.min(total, total - mod) + start; var word = 0; for (var i = start; i < end; i += limbLen) { word = parseBase(number, i, i + limbLen, base); this.imuln(limbPow); if (this.words[0] + word < 0x4000000) { this.words[0] += word; } else { this._iaddn(word); } } if (mod !== 0) { var pow = 1; word = parseBase(number, i, number.length, base); for (i = 0; i < mod; i++) { pow *= base; } this.imuln(pow); if (this.words[0] + word < 0x4000000) { this.words[0] += word; } else { this._iaddn(word); } } }; BN.prototype.copy = function copy (dest) { dest.words = new Array(this.length); for (var i = 0; i < this.length; i++) { dest.words[i] = this.words[i]; } dest.length = this.length; dest.negative = this.negative; dest.red = this.red; }; BN.prototype._move = function _move (dest) { dest.words = this.words; dest.length = this.length; dest.negative = this.negative; dest.red = this.red; }; BN.prototype.clone = function clone () { var r = new BN(null); this.copy(r); return r; }; BN.prototype._expand = function _expand (size) { while (this.length < size) { this.words[this.length++] = 0; } return this; }; // Remove leading `0` from `this` BN.prototype._strip = function strip () { while (this.length > 1 && this.words[this.length - 1] === 0) { this.length--; } return this._normSign(); }; BN.prototype._normSign = function _normSign () { // -0 = 0 if (this.length === 1 && this.words[0] === 0) { this.negative = 0; } return this; }; // Check Symbol.for because not everywhere where Symbol defined // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility if (typeof Symbol !== 'undefined' && typeof Symbol.for === 'function') { BN.prototype[Symbol.for('nodejs.util.inspect.custom')] = inspect; } else { BN.prototype.inspect = inspect; } function inspect () { return (this.red ? ''; } /* var zeros = []; var groupSizes = []; var groupBases = []; var s = ''; var i = -1; while (++i < BN.wordSize) { zeros[i] = s; s += '0'; } groupSizes[0] = 0; groupSizes[1] = 0; groupBases[0] = 0; groupBases[1] = 0; var base = 2 - 1; while (++base < 36 + 1) { var groupSize = 0; var groupBase = 1; while (groupBase < (1 << BN.wordSize) / base) { groupBase *= base; groupSize += 1; } groupSizes[base] = groupSize; groupBases[base] = groupBase; } */ var zeros = [ '', '0', '00', '000', '0000', '00000', '000000', '0000000', '00000000', '000000000', '0000000000', '00000000000', '000000000000', '0000000000000', '00000000000000', '000000000000000', '0000000000000000', '00000000000000000', '000000000000000000', '0000000000000000000', '00000000000000000000', '000000000000000000000', '0000000000000000000000', '00000000000000000000000', '000000000000000000000000', '0000000000000000000000000' ]; var groupSizes = [ 0, 0, 25, 16, 12, 11, 10, 9, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 ]; var groupBases = [ 0, 0, 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 ]; BN.prototype.toString = function toString (base, padding) { base = base || 10; padding = padding | 0 || 1; var out; if (base === 16 || base === 'hex') { out = ''; var off = 0; var carry = 0; for (var i = 0; i < this.length; i++) { var w = this.words[i]; var word = (((w << off) | carry) & 0xffffff).toString(16); carry = (w >>> (24 - off)) & 0xffffff; if (carry !== 0 || i !== this.length - 1) { out = zeros[6 - word.length] + word + out; } else { out = word + out; } off += 2; if (off >= 26) { off -= 26; i--; } } if (carry !== 0) { out = carry.toString(16) + out; } while (out.length % padding !== 0) { out = '0' + out; } if (this.negative !== 0) { out = '-' + out; } return out; } if (base === (base | 0) && base >= 2 && base <= 36) { // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); var groupSize = groupSizes[base]; // var groupBase = Math.pow(base, groupSize); var groupBase = groupBases[base]; out = ''; var c = this.clone(); c.negative = 0; while (!c.isZero()) { var r = c.modrn(groupBase).toString(base); c = c.idivn(groupBase); if (!c.isZero()) { out = zeros[groupSize - r.length] + r + out; } else { out = r + out; } } if (this.isZero()) { out = '0' + out; } while (out.length % padding !== 0) { out = '0' + out; } if (this.negative !== 0) { out = '-' + out; } return out; } assert(false, 'Base should be between 2 and 36'); }; BN.prototype.toNumber = function toNumber () { var ret = this.words[0]; if (this.length === 2) { ret += this.words[1] * 0x4000000; } else if (this.length === 3 && this.words[2] === 0x01) { // NOTE: at this stage it is known that the top bit is set ret += 0x10000000000000 + (this.words[1] * 0x4000000); } else if (this.length > 2) { assert(false, 'Number can only safely store up to 53 bits'); } return (this.negative !== 0) ? -ret : ret; }; BN.prototype.toJSON = function toJSON () { return this.toString(16, 2); }; if (Buffer) { BN.prototype.toBuffer = function toBuffer (endian, length) { return this.toArrayLike(Buffer, endian, length); }; } BN.prototype.toArray = function toArray (endian, length) { return this.toArrayLike(Array, endian, length); }; var allocate = function allocate (ArrayType, size) { if (ArrayType.allocUnsafe) { return ArrayType.allocUnsafe(size); } return new ArrayType(size); }; BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { this._strip(); var byteLength = this.byteLength(); var reqLength = length || Math.max(1, byteLength); assert(byteLength <= reqLength, 'byte array longer than desired length'); assert(reqLength > 0, 'Requested array length <= 0'); var res = allocate(ArrayType, reqLength); var postfix = endian === 'le' ? 'LE' : 'BE'; this['_toArrayLike' + postfix](res, byteLength); return res; }; BN.prototype._toArrayLikeLE = function _toArrayLikeLE (res, byteLength) { var position = 0; var carry = 0; for (var i = 0, shift = 0; i < this.length; i++) { var word = (this.words[i] << shift) | carry; res[position++] = word & 0xff; if (position < res.length) { res[position++] = (word >> 8) & 0xff; } if (position < res.length) { res[position++] = (word >> 16) & 0xff; } if (shift === 6) { if (position < res.length) { res[position++] = (word >> 24) & 0xff; } carry = 0; shift = 0; } else { carry = word >>> 24; shift += 2; } } if (position < res.length) { res[position++] = carry; while (position < res.length) { res[position++] = 0; } } }; BN.prototype._toArrayLikeBE = function _toArrayLikeBE (res, byteLength) { var position = res.length - 1; var carry = 0; for (var i = 0, shift = 0; i < this.length; i++) { var word = (this.words[i] << shift) | carry; res[position--] = word & 0xff; if (position >= 0) { res[position--] = (word >> 8) & 0xff; } if (position >= 0) { res[position--] = (word >> 16) & 0xff; } if (shift === 6) { if (position >= 0) { res[position--] = (word >> 24) & 0xff; } carry = 0; shift = 0; } else { carry = word >>> 24; shift += 2; } } if (position >= 0) { res[position--] = carry; while (position >= 0) { res[position--] = 0; } } }; if (Math.clz32) { BN.prototype._countBits = function _countBits (w) { return 32 - Math.clz32(w); }; } else { BN.prototype._countBits = function _countBits (w) { var t = w; var r = 0; if (t >= 0x1000) { r += 13; t >>>= 13; } if (t >= 0x40) { r += 7; t >>>= 7; } if (t >= 0x8) { r += 4; t >>>= 4; } if (t >= 0x02) { r += 2; t >>>= 2; } return r + t; }; } BN.prototype._zeroBits = function _zeroBits (w) { // Short-cut if (w === 0) return 26; var t = w; var r = 0; if ((t & 0x1fff) === 0) { r += 13; t >>>= 13; } if ((t & 0x7f) === 0) { r += 7; t >>>= 7; } if ((t & 0xf) === 0) { r += 4; t >>>= 4; } if ((t & 0x3) === 0) { r += 2; t >>>= 2; } if ((t & 0x1) === 0) { r++; } return r; }; // Return number of used bits in a BN BN.prototype.bitLength = function bitLength () { var w = this.words[this.length - 1]; var hi = this._countBits(w); return (this.length - 1) * 26 + hi; }; function toBitArray (num) { var w = new Array(num.bitLength()); for (var bit = 0; bit < w.length; bit++) { var off = (bit / 26) | 0; var wbit = bit % 26; w[bit] = (num.words[off] >>> wbit) & 0x01; } return w; } // Number of trailing zero bits BN.prototype.zeroBits = function zeroBits () { if (this.isZero()) return 0; var r = 0; for (var i = 0; i < this.length; i++) { var b = this._zeroBits(this.words[i]); r += b; if (b !== 26) break; } return r; }; BN.prototype.byteLength = function byteLength () { return Math.ceil(this.bitLength() / 8); }; BN.prototype.toTwos = function toTwos (width) { if (this.negative !== 0) { return this.abs().inotn(width).iaddn(1); } return this.clone(); }; BN.prototype.fromTwos = function fromTwos (width) { if (this.testn(width - 1)) { return this.notn(width).iaddn(1).ineg(); } return this.clone(); }; BN.prototype.isNeg = function isNeg () { return this.negative !== 0; }; // Return negative clone of `this` BN.prototype.neg = function neg () { return this.clone().ineg(); }; BN.prototype.ineg = function ineg () { if (!this.isZero()) { this.negative ^= 1; } return this; }; // Or `num` with `this` in-place BN.prototype.iuor = function iuor (num) { while (this.length < num.length) { this.words[this.length++] = 0; } for (var i = 0; i < num.length; i++) { this.words[i] = this.words[i] | num.words[i]; } return this._strip(); }; BN.prototype.ior = function ior (num) { assert((this.negative | num.negative) === 0); return this.iuor(num); }; // Or `num` with `this` BN.prototype.or = function or (num) { if (this.length > num.length) return this.clone().ior(num); return num.clone().ior(this); }; BN.prototype.uor = function uor (num) { if (this.length > num.length) return this.clone().iuor(num); return num.clone().iuor(this); }; // And `num` with `this` in-place BN.prototype.iuand = function iuand (num) { // b = min-length(num, this) var b; if (this.length > num.length) { b = num; } else { b = this; } for (var i = 0; i < b.length; i++) { this.words[i] = this.words[i] & num.words[i]; } this.length = b.length; return this._strip(); }; BN.prototype.iand = function iand (num) { assert((this.negative | num.negative) === 0); return this.iuand(num); }; // And `num` with `this` BN.prototype.and = function and (num) { if (this.length > num.length) return this.clone().iand(num); return num.clone().iand(this); }; BN.prototype.uand = function uand (num) { if (this.length > num.length) return this.clone().iuand(num); return num.clone().iuand(this); }; // Xor `num` with `this` in-place BN.prototype.iuxor = function iuxor (num) { // a.length > b.length var a; var b; if (this.length > num.length) { a = this; b = num; } else { a = num; b = this; } for (var i = 0; i < b.length; i++) { this.words[i] = a.words[i] ^ b.words[i]; } if (this !== a) { for (; i < a.length; i++) { this.words[i] = a.words[i]; } } this.length = a.length; return this._strip(); }; BN.prototype.ixor = function ixor (num) { assert((this.negative | num.negative) === 0); return this.iuxor(num); }; // Xor `num` with `this` BN.prototype.xor = function xor (num) { if (this.length > num.length) return this.clone().ixor(num); return num.clone().ixor(this); }; BN.prototype.uxor = function uxor (num) { if (this.length > num.length) return this.clone().iuxor(num); return num.clone().iuxor(this); }; // Not ``this`` with ``width`` bitwidth BN.prototype.inotn = function inotn (width) { assert(typeof width === 'number' && width >= 0); var bytesNeeded = Math.ceil(width / 26) | 0; var bitsLeft = width % 26; // Extend the buffer with leading zeroes this._expand(bytesNeeded); if (bitsLeft > 0) { bytesNeeded--; } // Handle complete words for (var i = 0; i < bytesNeeded; i++) { this.words[i] = ~this.words[i] & 0x3ffffff; } // Handle the residue if (bitsLeft > 0) { this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); } // And remove leading zeroes return this._strip(); }; BN.prototype.notn = function notn (width) { return this.clone().inotn(width); }; // Set `bit` of `this` BN.prototype.setn = function setn (bit, val) { assert(typeof bit === 'number' && bit >= 0); var off = (bit / 26) | 0; var wbit = bit % 26; this._expand(off + 1); if (val) { this.words[off] = this.words[off] | (1 << wbit); } else { this.words[off] = this.words[off] & ~(1 << wbit); } return this._strip(); }; // Add `num` to `this` in-place BN.prototype.iadd = function iadd (num) { var r; // negative + positive if (this.negative !== 0 && num.negative === 0) { this.negative = 0; r = this.isub(num); this.negative ^= 1; return this._normSign(); // positive + negative } else if (this.negative === 0 && num.negative !== 0) { num.negative = 0; r = this.isub(num); num.negative = 1; return r._normSign(); } // a.length > b.length var a, b; if (this.length > num.length) { a = this; b = num; } else { a = num; b = this; } var carry = 0; for (var i = 0; i < b.length; i++) { r = (a.words[i] | 0) + (b.words[i] | 0) + carry; this.words[i] = r & 0x3ffffff; carry = r >>> 26; } for (; carry !== 0 && i < a.length; i++) { r = (a.words[i] | 0) + carry; this.words[i] = r & 0x3ffffff; carry = r >>> 26; } this.length = a.length; if (carry !== 0) { this.words[this.length] = carry; this.length++; // Copy the rest of the words } else if (a !== this) { for (; i < a.length; i++) { this.words[i] = a.words[i]; } } return this; }; // Add `num` to `this` BN.prototype.add = function add (num) { var res; if (num.negative !== 0 && this.negative === 0) { num.negative = 0; res = this.sub(num); num.negative ^= 1; return res; } else if (num.negative === 0 && this.negative !== 0) { this.negative = 0; res = num.sub(this); this.negative = 1; return res; } if (this.length > num.length) return this.clone().iadd(num); return num.clone().iadd(this); }; // Subtract `num` from `this` in-place BN.prototype.isub = function isub (num) { // this - (-num) = this + num if (num.negative !== 0) { num.negative = 0; var r = this.iadd(num); num.negative = 1; return r._normSign(); // -this - num = -(this + num) } else if (this.negative !== 0) { this.negative = 0; this.iadd(num); this.negative = 1; return this._normSign(); } // At this point both numbers are positive var cmp = this.cmp(num); // Optimization - zeroify if (cmp === 0) { this.negative = 0; this.length = 1; this.words[0] = 0; return this; } // a > b var a, b; if (cmp > 0) { a = this; b = num; } else { a = num; b = this; } var carry = 0; for (var i = 0; i < b.length; i++) { r = (a.words[i] | 0) - (b.words[i] | 0) + carry; carry = r >> 26; this.words[i] = r & 0x3ffffff; } for (; carry !== 0 && i < a.length; i++) { r = (a.words[i] | 0) + carry; carry = r >> 26; this.words[i] = r & 0x3ffffff; } // Copy rest of the words if (carry === 0 && i < a.length && a !== this) { for (; i < a.length; i++) { this.words[i] = a.words[i]; } } this.length = Math.max(this.length, i); if (a !== this) { this.negative = 1; } return this._strip(); }; // Subtract `num` from `this` BN.prototype.sub = function sub (num) { return this.clone().isub(num); }; function smallMulTo (self, num, out) { out.negative = num.negative ^ self.negative; var len = (self.length + num.length) | 0; out.length = len; len = (len - 1) | 0; // Peel one iteration (compiler can't do it, because of code complexity) var a = self.words[0] | 0; var b = num.words[0] | 0; var r = a * b; var lo = r & 0x3ffffff; var carry = (r / 0x4000000) | 0; out.words[0] = lo; for (var k = 1; k < len; k++) { // Sum all words with the same `i + j = k` and accumulate `ncarry`, // note that ncarry could be >= 0x3ffffff var ncarry = carry >>> 26; var rword = carry & 0x3ffffff; var maxJ = Math.min(k, num.length - 1); for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { var i = (k - j) | 0; a = self.words[i] | 0; b = num.words[j] | 0; r = a * b + rword; ncarry += (r / 0x4000000) | 0; rword = r & 0x3ffffff; } out.words[k] = rword | 0; carry = ncarry | 0; } if (carry !== 0) { out.words[k] = carry | 0; } else { out.length--; } return out._strip(); } // TODO(indutny): it may be reasonable to omit it for users who don't need // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit // multiplication (like elliptic secp256k1). var comb10MulTo = function comb10MulTo (self, num, out) { var a = self.words; var b = num.words; var o = out.words; var c = 0; var lo; var mid; var hi; var a0 = a[0] | 0; var al0 = a0 & 0x1fff; var ah0 = a0 >>> 13; var a1 = a[1] | 0; var al1 = a1 & 0x1fff; var ah1 = a1 >>> 13; var a2 = a[2] | 0; var al2 = a2 & 0x1fff; var ah2 = a2 >>> 13; var a3 = a[3] | 0; var al3 = a3 & 0x1fff; var ah3 = a3 >>> 13; var a4 = a[4] | 0; var al4 = a4 & 0x1fff; var ah4 = a4 >>> 13; var a5 = a[5] | 0; var al5 = a5 & 0x1fff; var ah5 = a5 >>> 13; var a6 = a[6] | 0; var al6 = a6 & 0x1fff; var ah6 = a6 >>> 13; var a7 = a[7] | 0; var al7 = a7 & 0x1fff; var ah7 = a7 >>> 13; var a8 = a[8] | 0; var al8 = a8 & 0x1fff; var ah8 = a8 >>> 13; var a9 = a[9] | 0; var al9 = a9 & 0x1fff; var ah9 = a9 >>> 13; var b0 = b[0] | 0; var bl0 = b0 & 0x1fff; var bh0 = b0 >>> 13; var b1 = b[1] | 0; var bl1 = b1 & 0x1fff; var bh1 = b1 >>> 13; var b2 = b[2] | 0; var bl2 = b2 & 0x1fff; var bh2 = b2 >>> 13; var b3 = b[3] | 0; var bl3 = b3 & 0x1fff; var bh3 = b3 >>> 13; var b4 = b[4] | 0; var bl4 = b4 & 0x1fff; var bh4 = b4 >>> 13; var b5 = b[5] | 0; var bl5 = b5 & 0x1fff; var bh5 = b5 >>> 13; var b6 = b[6] | 0; var bl6 = b6 & 0x1fff; var bh6 = b6 >>> 13; var b7 = b[7] | 0; var bl7 = b7 & 0x1fff; var bh7 = b7 >>> 13; var b8 = b[8] | 0; var bl8 = b8 & 0x1fff; var bh8 = b8 >>> 13; var b9 = b[9] | 0; var bl9 = b9 & 0x1fff; var bh9 = b9 >>> 13; out.negative = self.negative ^ num.negative; out.length = 19; /* k = 0 */ lo = Math.imul(al0, bl0); mid = Math.imul(al0, bh0); mid = (mid + Math.imul(ah0, bl0)) | 0; hi = Math.imul(ah0, bh0); var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; w0 &= 0x3ffffff; /* k = 1 */ lo = Math.imul(al1, bl0); mid = Math.imul(al1, bh0); mid = (mid + Math.imul(ah1, bl0)) | 0; hi = Math.imul(ah1, bh0); lo = (lo + Math.imul(al0, bl1)) | 0; mid = (mid + Math.imul(al0, bh1)) | 0; mid = (mid + Math.imul(ah0, bl1)) | 0; hi = (hi + Math.imul(ah0, bh1)) | 0; var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; w1 &= 0x3ffffff; /* k = 2 */ lo = Math.imul(al2, bl0); mid = Math.imul(al2, bh0); mid = (mid + Math.imul(ah2, bl0)) | 0; hi = Math.imul(ah2, bh0); lo = (lo + Math.imul(al1, bl1)) | 0; mid = (mid + Math.imul(al1, bh1)) | 0; mid = (mid + Math.imul(ah1, bl1)) | 0; hi = (hi + Math.imul(ah1, bh1)) | 0; lo = (lo + Math.imul(al0, bl2)) | 0; mid = (mid + Math.imul(al0, bh2)) | 0; mid = (mid + Math.imul(ah0, bl2)) | 0; hi = (hi + Math.imul(ah0, bh2)) | 0; var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; w2 &= 0x3ffffff; /* k = 3 */ lo = Math.imul(al3, bl0); mid = Math.imul(al3, bh0); mid = (mid + Math.imul(ah3, bl0)) | 0; hi = Math.imul(ah3, bh0); lo = (lo + Math.imul(al2, bl1)) | 0; mid = (mid + Math.imul(al2, bh1)) | 0; mid = (mid + Math.imul(ah2, bl1)) | 0; hi = (hi + Math.imul(ah2, bh1)) | 0; lo = (lo + Math.imul(al1, bl2)) | 0; mid = (mid + Math.imul(al1, bh2)) | 0; mid = (mid + Math.imul(ah1, bl2)) | 0; hi = (hi + Math.imul(ah1, bh2)) | 0; lo = (lo + Math.imul(al0, bl3)) | 0; mid = (mid + Math.imul(al0, bh3)) | 0; mid = (mid + Math.imul(ah0, bl3)) | 0; hi = (hi + Math.imul(ah0, bh3)) | 0; var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; w3 &= 0x3ffffff; /* k = 4 */ lo = Math.imul(al4, bl0); mid = Math.imul(al4, bh0); mid = (mid + Math.imul(ah4, bl0)) | 0; hi = Math.imul(ah4, bh0); lo = (lo + Math.imul(al3, bl1)) | 0; mid = (mid + Math.imul(al3, bh1)) | 0; mid = (mid + Math.imul(ah3, bl1)) | 0; hi = (hi + Math.imul(ah3, bh1)) | 0; lo = (lo + Math.imul(al2, bl2)) | 0; mid = (mid + Math.imul(al2, bh2)) | 0; mid = (mid + Math.imul(ah2, bl2)) | 0; hi = (hi + Math.imul(ah2, bh2)) | 0; lo = (lo + Math.imul(al1, bl3)) | 0; mid = (mid + Math.imul(al1, bh3)) | 0; mid = (mid + Math.imul(ah1, bl3)) | 0; hi = (hi + Math.imul(ah1, bh3)) | 0; lo = (lo + Math.imul(al0, bl4)) | 0; mid = (mid + Math.imul(al0, bh4)) | 0; mid = (mid + Math.imul(ah0, bl4)) | 0; hi = (hi + Math.imul(ah0, bh4)) | 0; var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; w4 &= 0x3ffffff; /* k = 5 */ lo = Math.imul(al5, bl0); mid = Math.imul(al5, bh0); mid = (mid + Math.imul(ah5, bl0)) | 0; hi = Math.imul(ah5, bh0); lo = (lo + Math.imul(al4, bl1)) | 0; mid = (mid + Math.imul(al4, bh1)) | 0; mid = (mid + Math.imul(ah4, bl1)) | 0; hi = (hi + Math.imul(ah4, bh1)) | 0; lo = (lo + Math.imul(al3, bl2)) | 0; mid = (mid + Math.imul(al3, bh2)) | 0; mid = (mid + Math.imul(ah3, bl2)) | 0; hi = (hi + Math.imul(ah3, bh2)) | 0; lo = (lo + Math.imul(al2, bl3)) | 0; mid = (mid + Math.imul(al2, bh3)) | 0; mid = (mid + Math.imul(ah2, bl3)) | 0; hi = (hi + Math.imul(ah2, bh3)) | 0; lo = (lo + Math.imul(al1, bl4)) | 0; mid = (mid + Math.imul(al1, bh4)) | 0; mid = (mid + Math.imul(ah1, bl4)) | 0; hi = (hi + Math.imul(ah1, bh4)) | 0; lo = (lo + Math.imul(al0, bl5)) | 0; mid = (mid + Math.imul(al0, bh5)) | 0; mid = (mid + Math.imul(ah0, bl5)) | 0; hi = (hi + Math.imul(ah0, bh5)) | 0; var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; w5 &= 0x3ffffff; /* k = 6 */ lo = Math.imul(al6, bl0); mid = Math.imul(al6, bh0); mid = (mid + Math.imul(ah6, bl0)) | 0; hi = Math.imul(ah6, bh0); lo = (lo + Math.imul(al5, bl1)) | 0; mid = (mid + Math.imul(al5, bh1)) | 0; mid = (mid + Math.imul(ah5, bl1)) | 0; hi = (hi + Math.imul(ah5, bh1)) | 0; lo = (lo + Math.imul(al4, bl2)) | 0; mid = (mid + Math.imul(al4, bh2)) | 0; mid = (mid + Math.imul(ah4, bl2)) | 0; hi = (hi + Math.imul(ah4, bh2)) | 0; lo = (lo + Math.imul(al3, bl3)) | 0; mid = (mid + Math.imul(al3, bh3)) | 0; mid = (mid + Math.imul(ah3, bl3)) | 0; hi = (hi + Math.imul(ah3, bh3)) | 0; lo = (lo + Math.imul(al2, bl4)) | 0; mid = (mid + Math.imul(al2, bh4)) | 0; mid = (mid + Math.imul(ah2, bl4)) | 0; hi = (hi + Math.imul(ah2, bh4)) | 0; lo = (lo + Math.imul(al1, bl5)) | 0; mid = (mid + Math.imul(al1, bh5)) | 0; mid = (mid + Math.imul(ah1, bl5)) | 0; hi = (hi + Math.imul(ah1, bh5)) | 0; lo = (lo + Math.imul(al0, bl6)) | 0; mid = (mid + Math.imul(al0, bh6)) | 0; mid = (mid + Math.imul(ah0, bl6)) | 0; hi = (hi + Math.imul(ah0, bh6)) | 0; var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; w6 &= 0x3ffffff; /* k = 7 */ lo = Math.imul(al7, bl0); mid = Math.imul(al7, bh0); mid = (mid + Math.imul(ah7, bl0)) | 0; hi = Math.imul(ah7, bh0); lo = (lo + Math.imul(al6, bl1)) | 0; mid = (mid + Math.imul(al6, bh1)) | 0; mid = (mid + Math.imul(ah6, bl1)) | 0; hi = (hi + Math.imul(ah6, bh1)) | 0; lo = (lo + Math.imul(al5, bl2)) | 0; mid = (mid + Math.imul(al5, bh2)) | 0; mid = (mid + Math.imul(ah5, bl2)) | 0; hi = (hi + Math.imul(ah5, bh2)) | 0; lo = (lo + Math.imul(al4, bl3)) | 0; mid = (mid + Math.imul(al4, bh3)) | 0; mid = (mid + Math.imul(ah4, bl3)) | 0; hi = (hi + Math.imul(ah4, bh3)) | 0; lo = (lo + Math.imul(al3, bl4)) | 0; mid = (mid + Math.imul(al3, bh4)) | 0; mid = (mid + Math.imul(ah3, bl4)) | 0; hi = (hi + Math.imul(ah3, bh4)) | 0; lo = (lo + Math.imul(al2, bl5)) | 0; mid = (mid + Math.imul(al2, bh5)) | 0; mid = (mid + Math.imul(ah2, bl5)) | 0; hi = (hi + Math.imul(ah2, bh5)) | 0; lo = (lo + Math.imul(al1, bl6)) | 0; mid = (mid + Math.imul(al1, bh6)) | 0; mid = (mid + Math.imul(ah1, bl6)) | 0; hi = (hi + Math.imul(ah1, bh6)) | 0; lo = (lo + Math.imul(al0, bl7)) | 0; mid = (mid + Math.imul(al0, bh7)) | 0; mid = (mid + Math.imul(ah0, bl7)) | 0; hi = (hi + Math.imul(ah0, bh7)) | 0; var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; w7 &= 0x3ffffff; /* k = 8 */ lo = Math.imul(al8, bl0); mid = Math.imul(al8, bh0); mid = (mid + Math.imul(ah8, bl0)) | 0; hi = Math.imul(ah8, bh0); lo = (lo + Math.imul(al7, bl1)) | 0; mid = (mid + Math.imul(al7, bh1)) | 0; mid = (mid + Math.imul(ah7, bl1)) | 0; hi = (hi + Math.imul(ah7, bh1)) | 0; lo = (lo + Math.imul(al6, bl2)) | 0; mid = (mid + Math.imul(al6, bh2)) | 0; mid = (mid + Math.imul(ah6, bl2)) | 0; hi = (hi + Math.imul(ah6, bh2)) | 0; lo = (lo + Math.imul(al5, bl3)) | 0; mid = (mid + Math.imul(al5, bh3)) | 0; mid = (mid + Math.imul(ah5, bl3)) | 0; hi = (hi + Math.imul(ah5, bh3)) | 0; lo = (lo + Math.imul(al4, bl4)) | 0; mid = (mid + Math.imul(al4, bh4)) | 0; mid = (mid + Math.imul(ah4, bl4)) | 0; hi = (hi + Math.imul(ah4, bh4)) | 0; lo = (lo + Math.imul(al3, bl5)) | 0; mid = (mid + Math.imul(al3, bh5)) | 0; mid = (mid + Math.imul(ah3, bl5)) | 0; hi = (hi + Math.imul(ah3, bh5)) | 0; lo = (lo + Math.imul(al2, bl6)) | 0; mid = (mid + Math.imul(al2, bh6)) | 0; mid = (mid + Math.imul(ah2, bl6)) | 0; hi = (hi + Math.imul(ah2, bh6)) | 0; lo = (lo + Math.imul(al1, bl7)) | 0; mid = (mid + Math.imul(al1, bh7)) | 0; mid = (mid + Math.imul(ah1, bl7)) | 0; hi = (hi + Math.imul(ah1, bh7)) | 0; lo = (lo + Math.imul(al0, bl8)) | 0; mid = (mid + Math.imul(al0, bh8)) | 0; mid = (mid + Math.imul(ah0, bl8)) | 0; hi = (hi + Math.imul(ah0, bh8)) | 0; var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; w8 &= 0x3ffffff; /* k = 9 */ lo = Math.imul(al9, bl0); mid = Math.imul(al9, bh0); mid = (mid + Math.imul(ah9, bl0)) | 0; hi = Math.imul(ah9, bh0); lo = (lo + Math.imul(al8, bl1)) | 0; mid = (mid + Math.imul(al8, bh1)) | 0; mid = (mid + Math.imul(ah8, bl1)) | 0; hi = (hi + Math.imul(ah8, bh1)) | 0; lo = (lo + Math.imul(al7, bl2)) | 0; mid = (mid + Math.imul(al7, bh2)) | 0; mid = (mid + Math.imul(ah7, bl2)) | 0; hi = (hi + Math.imul(ah7, bh2)) | 0; lo = (lo + Math.imul(al6, bl3)) | 0; mid = (mid + Math.imul(al6, bh3)) | 0; mid = (mid + Math.imul(ah6, bl3)) | 0; hi = (hi + Math.imul(ah6, bh3)) | 0; lo = (lo + Math.imul(al5, bl4)) | 0; mid = (mid + Math.imul(al5, bh4)) | 0; mid = (mid + Math.imul(ah5, bl4)) | 0; hi = (hi + Math.imul(ah5, bh4)) | 0; lo = (lo + Math.imul(al4, bl5)) | 0; mid = (mid + Math.imul(al4, bh5)) | 0; mid = (mid + Math.imul(ah4, bl5)) | 0; hi = (hi + Math.imul(ah4, bh5)) | 0; lo = (lo + Math.imul(al3, bl6)) | 0; mid = (mid + Math.imul(al3, bh6)) | 0; mid = (mid + Math.imul(ah3, bl6)) | 0; hi = (hi + Math.imul(ah3, bh6)) | 0; lo = (lo + Math.imul(al2, bl7)) | 0; mid = (mid + Math.imul(al2, bh7)) | 0; mid = (mid + Math.imul(ah2, bl7)) | 0; hi = (hi + Math.imul(ah2, bh7)) | 0; lo = (lo + Math.imul(al1, bl8)) | 0; mid = (mid + Math.imul(al1, bh8)) | 0; mid = (mid + Math.imul(ah1, bl8)) | 0; hi = (hi + Math.imul(ah1, bh8)) | 0; lo = (lo + Math.imul(al0, bl9)) | 0; mid = (mid + Math.imul(al0, bh9)) | 0; mid = (mid + Math.imul(ah0, bl9)) | 0; hi = (hi + Math.imul(ah0, bh9)) | 0; var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; w9 &= 0x3ffffff; /* k = 10 */ lo = Math.imul(al9, bl1); mid = Math.imul(al9, bh1); mid = (mid + Math.imul(ah9, bl1)) | 0; hi = Math.imul(ah9, bh1); lo = (lo + Math.imul(al8, bl2)) | 0; mid = (mid + Math.imul(al8, bh2)) | 0; mid = (mid + Math.imul(ah8, bl2)) | 0; hi = (hi + Math.imul(ah8, bh2)) | 0; lo = (lo + Math.imul(al7, bl3)) | 0; mid = (mid + Math.imul(al7, bh3)) | 0; mid = (mid + Math.imul(ah7, bl3)) | 0; hi = (hi + Math.imul(ah7, bh3)) | 0; lo = (lo + Math.imul(al6, bl4)) | 0; mid = (mid + Math.imul(al6, bh4)) | 0; mid = (mid + Math.imul(ah6, bl4)) | 0; hi = (hi + Math.imul(ah6, bh4)) | 0; lo = (lo + Math.imul(al5, bl5)) | 0; mid = (mid + Math.imul(al5, bh5)) | 0; mid = (mid + Math.imul(ah5, bl5)) | 0; hi = (hi + Math.imul(ah5, bh5)) | 0; lo = (lo + Math.imul(al4, bl6)) | 0; mid = (mid + Math.imul(al4, bh6)) | 0; mid = (mid + Math.imul(ah4, bl6)) | 0; hi = (hi + Math.imul(ah4, bh6)) | 0; lo = (lo + Math.imul(al3, bl7)) | 0; mid = (mid + Math.imul(al3, bh7)) | 0; mid = (mid + Math.imul(ah3, bl7)) | 0; hi = (hi + Math.imul(ah3, bh7)) | 0; lo = (lo + Math.imul(al2, bl8)) | 0; mid = (mid + Math.imul(al2, bh8)) | 0; mid = (mid + Math.imul(ah2, bl8)) | 0; hi = (hi + Math.imul(ah2, bh8)) | 0; lo = (lo + Math.imul(al1, bl9)) | 0; mid = (mid + Math.imul(al1, bh9)) | 0; mid = (mid + Math.imul(ah1, bl9)) | 0; hi = (hi + Math.imul(ah1, bh9)) | 0; var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; w10 &= 0x3ffffff; /* k = 11 */ lo = Math.imul(al9, bl2); mid = Math.imul(al9, bh2); mid = (mid + Math.imul(ah9, bl2)) | 0; hi = Math.imul(ah9, bh2); lo = (lo + Math.imul(al8, bl3)) | 0; mid = (mid + Math.imul(al8, bh3)) | 0; mid = (mid + Math.imul(ah8, bl3)) | 0; hi = (hi + Math.imul(ah8, bh3)) | 0; lo = (lo + Math.imul(al7, bl4)) | 0; mid = (mid + Math.imul(al7, bh4)) | 0; mid = (mid + Math.imul(ah7, bl4)) | 0; hi = (hi + Math.imul(ah7, bh4)) | 0; lo = (lo + Math.imul(al6, bl5)) | 0; mid = (mid + Math.imul(al6, bh5)) | 0; mid = (mid + Math.imul(ah6, bl5)) | 0; hi = (hi + Math.imul(ah6, bh5)) | 0; lo = (lo + Math.imul(al5, bl6)) | 0; mid = (mid + Math.imul(al5, bh6)) | 0; mid = (mid + Math.imul(ah5, bl6)) | 0; hi = (hi + Math.imul(ah5, bh6)) | 0; lo = (lo + Math.imul(al4, bl7)) | 0; mid = (mid + Math.imul(al4, bh7)) | 0; mid = (mid + Math.imul(ah4, bl7)) | 0; hi = (hi + Math.imul(ah4, bh7)) | 0; lo = (lo + Math.imul(al3, bl8)) | 0; mid = (mid + Math.imul(al3, bh8)) | 0; mid = (mid + Math.imul(ah3, bl8)) | 0; hi = (hi + Math.imul(ah3, bh8)) | 0; lo = (lo + Math.imul(al2, bl9)) | 0; mid = (mid + Math.imul(al2, bh9)) | 0; mid = (mid + Math.imul(ah2, bl9)) | 0; hi = (hi + Math.imul(ah2, bh9)) | 0; var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; w11 &= 0x3ffffff; /* k = 12 */ lo = Math.imul(al9, bl3); mid = Math.imul(al9, bh3); mid = (mid + Math.imul(ah9, bl3)) | 0; hi = Math.imul(ah9, bh3); lo = (lo + Math.imul(al8, bl4)) | 0; mid = (mid + Math.imul(al8, bh4)) | 0; mid = (mid + Math.imul(ah8, bl4)) | 0; hi = (hi + Math.imul(ah8, bh4)) | 0; lo = (lo + Math.imul(al7, bl5)) | 0; mid = (mid + Math.imul(al7, bh5)) | 0; mid = (mid + Math.imul(ah7, bl5)) | 0; hi = (hi + Math.imul(ah7, bh5)) | 0; lo = (lo + Math.imul(al6, bl6)) | 0; mid = (mid + Math.imul(al6, bh6)) | 0; mid = (mid + Math.imul(ah6, bl6)) | 0; hi = (hi + Math.imul(ah6, bh6)) | 0; lo = (lo + Math.imul(al5, bl7)) | 0; mid = (mid + Math.imul(al5, bh7)) | 0; mid = (mid + Math.imul(ah5, bl7)) | 0; hi = (hi + Math.imul(ah5, bh7)) | 0; lo = (lo + Math.imul(al4, bl8)) | 0; mid = (mid + Math.imul(al4, bh8)) | 0; mid = (mid + Math.imul(ah4, bl8)) | 0; hi = (hi + Math.imul(ah4, bh8)) | 0; lo = (lo + Math.imul(al3, bl9)) | 0; mid = (mid + Math.imul(al3, bh9)) | 0; mid = (mid + Math.imul(ah3, bl9)) | 0; hi = (hi + Math.imul(ah3, bh9)) | 0; var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; w12 &= 0x3ffffff; /* k = 13 */ lo = Math.imul(al9, bl4); mid = Math.imul(al9, bh4); mid = (mid + Math.imul(ah9, bl4)) | 0; hi = Math.imul(ah9, bh4); lo = (lo + Math.imul(al8, bl5)) | 0; mid = (mid + Math.imul(al8, bh5)) | 0; mid = (mid + Math.imul(ah8, bl5)) | 0; hi = (hi + Math.imul(ah8, bh5)) | 0; lo = (lo + Math.imul(al7, bl6)) | 0; mid = (mid + Math.imul(al7, bh6)) | 0; mid = (mid + Math.imul(ah7, bl6)) | 0; hi = (hi + Math.imul(ah7, bh6)) | 0; lo = (lo + Math.imul(al6, bl7)) | 0; mid = (mid + Math.imul(al6, bh7)) | 0; mid = (mid + Math.imul(ah6, bl7)) | 0; hi = (hi + Math.imul(ah6, bh7)) | 0; lo = (lo + Math.imul(al5, bl8)) | 0; mid = (mid + Math.imul(al5, bh8)) | 0; mid = (mid + Math.imul(ah5, bl8)) | 0; hi = (hi + Math.imul(ah5, bh8)) | 0; lo = (lo + Math.imul(al4, bl9)) | 0; mid = (mid + Math.imul(al4, bh9)) | 0; mid = (mid + Math.imul(ah4, bl9)) | 0; hi = (hi + Math.imul(ah4, bh9)) | 0; var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; w13 &= 0x3ffffff; /* k = 14 */ lo = Math.imul(al9, bl5); mid = Math.imul(al9, bh5); mid = (mid + Math.imul(ah9, bl5)) | 0; hi = Math.imul(ah9, bh5); lo = (lo + Math.imul(al8, bl6)) | 0; mid = (mid + Math.imul(al8, bh6)) | 0; mid = (mid + Math.imul(ah8, bl6)) | 0; hi = (hi + Math.imul(ah8, bh6)) | 0; lo = (lo + Math.imul(al7, bl7)) | 0; mid = (mid + Math.imul(al7, bh7)) | 0; mid = (mid + Math.imul(ah7, bl7)) | 0; hi = (hi + Math.imul(ah7, bh7)) | 0; lo = (lo + Math.imul(al6, bl8)) | 0; mid = (mid + Math.imul(al6, bh8)) | 0; mid = (mid + Math.imul(ah6, bl8)) | 0; hi = (hi + Math.imul(ah6, bh8)) | 0; lo = (lo + Math.imul(al5, bl9)) | 0; mid = (mid + Math.imul(al5, bh9)) | 0; mid = (mid + Math.imul(ah5, bl9)) | 0; hi = (hi + Math.imul(ah5, bh9)) | 0; var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; w14 &= 0x3ffffff; /* k = 15 */ lo = Math.imul(al9, bl6); mid = Math.imul(al9, bh6); mid = (mid + Math.imul(ah9, bl6)) | 0; hi = Math.imul(ah9, bh6); lo = (lo + Math.imul(al8, bl7)) | 0; mid = (mid + Math.imul(al8, bh7)) | 0; mid = (mid + Math.imul(ah8, bl7)) | 0; hi = (hi + Math.imul(ah8, bh7)) | 0; lo = (lo + Math.imul(al7, bl8)) | 0; mid = (mid + Math.imul(al7, bh8)) | 0; mid = (mid + Math.imul(ah7, bl8)) | 0; hi = (hi + Math.imul(ah7, bh8)) | 0; lo = (lo + Math.imul(al6, bl9)) | 0; mid = (mid + Math.imul(al6, bh9)) | 0; mid = (mid + Math.imul(ah6, bl9)) | 0; hi = (hi + Math.imul(ah6, bh9)) | 0; var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; w15 &= 0x3ffffff; /* k = 16 */ lo = Math.imul(al9, bl7); mid = Math.imul(al9, bh7); mid = (mid + Math.imul(ah9, bl7)) | 0; hi = Math.imul(ah9, bh7); lo = (lo + Math.imul(al8, bl8)) | 0; mid = (mid + Math.imul(al8, bh8)) | 0; mid = (mid + Math.imul(ah8, bl8)) | 0; hi = (hi + Math.imul(ah8, bh8)) | 0; lo = (lo + Math.imul(al7, bl9)) | 0; mid = (mid + Math.imul(al7, bh9)) | 0; mid = (mid + Math.imul(ah7, bl9)) | 0; hi = (hi + Math.imul(ah7, bh9)) | 0; var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; w16 &= 0x3ffffff; /* k = 17 */ lo = Math.imul(al9, bl8); mid = Math.imul(al9, bh8); mid = (mid + Math.imul(ah9, bl8)) | 0; hi = Math.imul(ah9, bh8); lo = (lo + Math.imul(al8, bl9)) | 0; mid = (mid + Math.imul(al8, bh9)) | 0; mid = (mid + Math.imul(ah8, bl9)) | 0; hi = (hi + Math.imul(ah8, bh9)) | 0; var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; w17 &= 0x3ffffff; /* k = 18 */ lo = Math.imul(al9, bl9); mid = Math.imul(al9, bh9); mid = (mid + Math.imul(ah9, bl9)) | 0; hi = Math.imul(ah9, bh9); var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; w18 &= 0x3ffffff; o[0] = w0; o[1] = w1; o[2] = w2; o[3] = w3; o[4] = w4; o[5] = w5; o[6] = w6; o[7] = w7; o[8] = w8; o[9] = w9; o[10] = w10; o[11] = w11; o[12] = w12; o[13] = w13; o[14] = w14; o[15] = w15; o[16] = w16; o[17] = w17; o[18] = w18; if (c !== 0) { o[19] = c; out.length++; } return out; }; // Polyfill comb if (!Math.imul) { comb10MulTo = smallMulTo; } function bigMulTo (self, num, out) { out.negative = num.negative ^ self.negative; out.length = self.length + num.length; var carry = 0; var hncarry = 0; for (var k = 0; k < out.length - 1; k++) { // Sum all words with the same `i + j = k` and accumulate `ncarry`, // note that ncarry could be >= 0x3ffffff var ncarry = hncarry; hncarry = 0; var rword = carry & 0x3ffffff; var maxJ = Math.min(k, num.length - 1); for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { var i = k - j; var a = self.words[i] | 0; var b = num.words[j] | 0; var r = a * b; var lo = r & 0x3ffffff; ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; lo = (lo + rword) | 0; rword = lo & 0x3ffffff; ncarry = (ncarry + (lo >>> 26)) | 0; hncarry += ncarry >>> 26; ncarry &= 0x3ffffff; } out.words[k] = rword; carry = ncarry; ncarry = hncarry; } if (carry !== 0) { out.words[k] = carry; } else { out.length--; } return out._strip(); } function jumboMulTo (self, num, out) { // Temporary disable, see https://github.com/indutny/bn.js/issues/211 // var fftm = new FFTM(); // return fftm.mulp(self, num, out); return bigMulTo(self, num, out); } BN.prototype.mulTo = function mulTo (num, out) { var res; var len = this.length + num.length; if (this.length === 10 && num.length === 10) { res = comb10MulTo(this, num, out); } else if (len < 63) { res = smallMulTo(this, num, out); } else if (len < 1024) { res = bigMulTo(this, num, out); } else { res = jumboMulTo(this, num, out); } return res; }; // Cooley-Tukey algorithm for FFT // slightly revisited to rely on looping instead of recursion function FFTM (x, y) { this.x = x; this.y = y; } FFTM.prototype.makeRBT = function makeRBT (N) { var t = new Array(N); var l = BN.prototype._countBits(N) - 1; for (var i = 0; i < N; i++) { t[i] = this.revBin(i, l, N); } return t; }; // Returns binary-reversed representation of `x` FFTM.prototype.revBin = function revBin (x, l, N) { if (x === 0 || x === N - 1) return x; var rb = 0; for (var i = 0; i < l; i++) { rb |= (x & 1) << (l - i - 1); x >>= 1; } return rb; }; // Performs "tweedling" phase, therefore 'emulating' // behaviour of the recursive algorithm FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { for (var i = 0; i < N; i++) { rtws[i] = rws[rbt[i]]; itws[i] = iws[rbt[i]]; } }; FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { this.permute(rbt, rws, iws, rtws, itws, N); for (var s = 1; s < N; s <<= 1) { var l = s << 1; var rtwdf = Math.cos(2 * Math.PI / l); var itwdf = Math.sin(2 * Math.PI / l); for (var p = 0; p < N; p += l) { var rtwdf_ = rtwdf; var itwdf_ = itwdf; for (var j = 0; j < s; j++) { var re = rtws[p + j]; var ie = itws[p + j]; var ro = rtws[p + j + s]; var io = itws[p + j + s]; var rx = rtwdf_ * ro - itwdf_ * io; io = rtwdf_ * io + itwdf_ * ro; ro = rx; rtws[p + j] = re + ro; itws[p + j] = ie + io; rtws[p + j + s] = re - ro; itws[p + j + s] = ie - io; /* jshint maxdepth : false */ if (j !== l) { rx = rtwdf * rtwdf_ - itwdf * itwdf_; itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; rtwdf_ = rx; } } } } }; FFTM.prototype.guessLen13b = function guessLen13b (n, m) { var N = Math.max(m, n) | 1; var odd = N & 1; var i = 0; for (N = N / 2 | 0; N; N = N >>> 1) { i++; } return 1 << i + 1 + odd; }; FFTM.prototype.conjugate = function conjugate (rws, iws, N) { if (N <= 1) return; for (var i = 0; i < N / 2; i++) { var t = rws[i]; rws[i] = rws[N - i - 1]; rws[N - i - 1] = t; t = iws[i]; iws[i] = -iws[N - i - 1]; iws[N - i - 1] = -t; } }; FFTM.prototype.normalize13b = function normalize13b (ws, N) { var carry = 0; for (var i = 0; i < N / 2; i++) { var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + Math.round(ws[2 * i] / N) + carry; ws[i] = w & 0x3ffffff; if (w < 0x4000000) { carry = 0; } else { carry = w / 0x4000000 | 0; } } return ws; }; FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { var carry = 0; for (var i = 0; i < len; i++) { carry = carry + (ws[i] | 0); rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; } // Pad with zeroes for (i = 2 * len; i < N; ++i) { rws[i] = 0; } assert(carry === 0); assert((carry & ~0x1fff) === 0); }; FFTM.prototype.stub = function stub (N) { var ph = new Array(N); for (var i = 0; i < N; i++) { ph[i] = 0; } return ph; }; FFTM.prototype.mulp = function mulp (x, y, out) { var N = 2 * this.guessLen13b(x.length, y.length); var rbt = this.makeRBT(N); var _ = this.stub(N); var rws = new Array(N); var rwst = new Array(N); var iwst = new Array(N); var nrws = new Array(N); var nrwst = new Array(N); var niwst = new Array(N); var rmws = out.words; rmws.length = N; this.convert13b(x.words, x.length, rws, N); this.convert13b(y.words, y.length, nrws, N); this.transform(rws, _, rwst, iwst, N, rbt); this.transform(nrws, _, nrwst, niwst, N, rbt); for (var i = 0; i < N; i++) { var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; rwst[i] = rx; } this.conjugate(rwst, iwst, N); this.transform(rwst, iwst, rmws, _, N, rbt); this.conjugate(rmws, _, N); this.normalize13b(rmws, N); out.negative = x.negative ^ y.negative; out.length = x.length + y.length; return out._strip(); }; // Multiply `this` by `num` BN.prototype.mul = function mul (num) { var out = new BN(null); out.words = new Array(this.length + num.length); return this.mulTo(num, out); }; // Multiply employing FFT BN.prototype.mulf = function mulf (num) { var out = new BN(null); out.words = new Array(this.length + num.length); return jumboMulTo(this, num, out); }; // In-place Multiplication BN.prototype.imul = function imul (num) { return this.clone().mulTo(num, this); }; BN.prototype.imuln = function imuln (num) { var isNegNum = num < 0; if (isNegNum) num = -num; assert(typeof num === 'number'); assert(num < 0x4000000); // Carry var carry = 0; for (var i = 0; i < this.length; i++) { var w = (this.words[i] | 0) * num; var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); carry >>= 26; carry += (w / 0x4000000) | 0; // NOTE: lo is 27bit maximum carry += lo >>> 26; this.words[i] = lo & 0x3ffffff; } if (carry !== 0) { this.words[i] = carry; this.length++; } return isNegNum ? this.ineg() : this; }; BN.prototype.muln = function muln (num) { return this.clone().imuln(num); }; // `this` * `this` BN.prototype.sqr = function sqr () { return this.mul(this); }; // `this` * `this` in-place BN.prototype.isqr = function isqr () { return this.imul(this.clone()); }; // Math.pow(`this`, `num`) BN.prototype.pow = function pow (num) { var w = toBitArray(num); if (w.length === 0) return new BN(1); // Skip leading zeroes var res = this; for (var i = 0; i < w.length; i++, res = res.sqr()) { if (w[i] !== 0) break; } if (++i < w.length) { for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { if (w[i] === 0) continue; res = res.mul(q); } } return res; }; // Shift-left in-place BN.prototype.iushln = function iushln (bits) { assert(typeof bits === 'number' && bits >= 0); var r = bits % 26; var s = (bits - r) / 26; var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); var i; if (r !== 0) { var carry = 0; for (i = 0; i < this.length; i++) { var newCarry = this.words[i] & carryMask; var c = ((this.words[i] | 0) - newCarry) << r; this.words[i] = c | carry; carry = newCarry >>> (26 - r); } if (carry) { this.words[i] = carry; this.length++; } } if (s !== 0) { for (i = this.length - 1; i >= 0; i--) { this.words[i + s] = this.words[i]; } for (i = 0; i < s; i++) { this.words[i] = 0; } this.length += s; } return this._strip(); }; BN.prototype.ishln = function ishln (bits) { // TODO(indutny): implement me assert(this.negative === 0); return this.iushln(bits); }; // Shift-right in-place // NOTE: `hint` is a lowest bit before trailing zeroes // NOTE: if `extended` is present - it will be filled with destroyed bits BN.prototype.iushrn = function iushrn (bits, hint, extended) { assert(typeof bits === 'number' && bits >= 0); var h; if (hint) { h = (hint - (hint % 26)) / 26; } else { h = 0; } var r = bits % 26; var s = Math.min((bits - r) / 26, this.length); var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); var maskedWords = extended; h -= s; h = Math.max(0, h); // Extended mode, copy masked part if (maskedWords) { for (var i = 0; i < s; i++) { maskedWords.words[i] = this.words[i]; } maskedWords.length = s; } if (s === 0) { // No-op, we should not move anything at all } else if (this.length > s) { this.length -= s; for (i = 0; i < this.length; i++) { this.words[i] = this.words[i + s]; } } else { this.words[0] = 0; this.length = 1; } var carry = 0; for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { var word = this.words[i] | 0; this.words[i] = (carry << (26 - r)) | (word >>> r); carry = word & mask; } // Push carried bits as a mask if (maskedWords && carry !== 0) { maskedWords.words[maskedWords.length++] = carry; } if (this.length === 0) { this.words[0] = 0; this.length = 1; } return this._strip(); }; BN.prototype.ishrn = function ishrn (bits, hint, extended) { // TODO(indutny): implement me assert(this.negative === 0); return this.iushrn(bits, hint, extended); }; // Shift-left BN.prototype.shln = function shln (bits) { return this.clone().ishln(bits); }; BN.prototype.ushln = function ushln (bits) { return this.clone().iushln(bits); }; // Shift-right BN.prototype.shrn = function shrn (bits) { return this.clone().ishrn(bits); }; BN.prototype.ushrn = function ushrn (bits) { return this.clone().iushrn(bits); }; // Test if n bit is set BN.prototype.testn = function testn (bit) { assert(typeof bit === 'number' && bit >= 0); var r = bit % 26; var s = (bit - r) / 26; var q = 1 << r; // Fast case: bit is much higher than all existing words if (this.length <= s) return false; // Check bit and return var w = this.words[s]; return !!(w & q); }; // Return only lowers bits of number (in-place) BN.prototype.imaskn = function imaskn (bits) { assert(typeof bits === 'number' && bits >= 0); var r = bits % 26; var s = (bits - r) / 26; assert(this.negative === 0, 'imaskn works only with positive numbers'); if (this.length <= s) { return this; } if (r !== 0) { s++; } this.length = Math.min(s, this.length); if (r !== 0) { var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); this.words[this.length - 1] &= mask; } return this._strip(); }; // Return only lowers bits of number BN.prototype.maskn = function maskn (bits) { return this.clone().imaskn(bits); }; // Add plain number `num` to `this` BN.prototype.iaddn = function iaddn (num) { assert(typeof num === 'number'); assert(num < 0x4000000); if (num < 0) return this.isubn(-num); // Possible sign change if (this.negative !== 0) { if (this.length === 1 && (this.words[0] | 0) <= num) { this.words[0] = num - (this.words[0] | 0); this.negative = 0; return this; } this.negative = 0; this.isubn(num); this.negative = 1; return this; } // Add without checks return this._iaddn(num); }; BN.prototype._iaddn = function _iaddn (num) { this.words[0] += num; // Carry for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { this.words[i] -= 0x4000000; if (i === this.length - 1) { this.words[i + 1] = 1; } else { this.words[i + 1]++; } } this.length = Math.max(this.length, i + 1); return this; }; // Subtract plain number `num` from `this` BN.prototype.isubn = function isubn (num) { assert(typeof num === 'number'); assert(num < 0x4000000); if (num < 0) return this.iaddn(-num); if (this.negative !== 0) { this.negative = 0; this.iaddn(num); this.negative = 1; return this; } this.words[0] -= num; if (this.length === 1 && this.words[0] < 0) { this.words[0] = -this.words[0]; this.negative = 1; } else { // Carry for (var i = 0; i < this.length && this.words[i] < 0; i++) { this.words[i] += 0x4000000; this.words[i + 1] -= 1; } } return this._strip(); }; BN.prototype.addn = function addn (num) { return this.clone().iaddn(num); }; BN.prototype.subn = function subn (num) { return this.clone().isubn(num); }; BN.prototype.iabs = function iabs () { this.negative = 0; return this; }; BN.prototype.abs = function abs () { return this.clone().iabs(); }; BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { var len = num.length + shift; var i; this._expand(len); var w; var carry = 0; for (i = 0; i < num.length; i++) { w = (this.words[i + shift] | 0) + carry; var right = (num.words[i] | 0) * mul; w -= right & 0x3ffffff; carry = (w >> 26) - ((right / 0x4000000) | 0); this.words[i + shift] = w & 0x3ffffff; } for (; i < this.length - shift; i++) { w = (this.words[i + shift] | 0) + carry; carry = w >> 26; this.words[i + shift] = w & 0x3ffffff; } if (carry === 0) return this._strip(); // Subtraction overflow assert(carry === -1); carry = 0; for (i = 0; i < this.length; i++) { w = -(this.words[i] | 0) + carry; carry = w >> 26; this.words[i] = w & 0x3ffffff; } this.negative = 1; return this._strip(); }; BN.prototype._wordDiv = function _wordDiv (num, mode) { var shift = this.length - num.length; var a = this.clone(); var b = num; // Normalize var bhi = b.words[b.length - 1] | 0; var bhiBits = this._countBits(bhi); shift = 26 - bhiBits; if (shift !== 0) { b = b.ushln(shift); a.iushln(shift); bhi = b.words[b.length - 1] | 0; } // Initialize quotient var m = a.length - b.length; var q; if (mode !== 'mod') { q = new BN(null); q.length = m + 1; q.words = new Array(q.length); for (var i = 0; i < q.length; i++) { q.words[i] = 0; } } var diff = a.clone()._ishlnsubmul(b, 1, m); if (diff.negative === 0) { a = diff; if (q) { q.words[m] = 1; } } for (var j = m - 1; j >= 0; j--) { var qj = (a.words[b.length + j] | 0) * 0x4000000 + (a.words[b.length + j - 1] | 0); // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max // (0x7ffffff) qj = Math.min((qj / bhi) | 0, 0x3ffffff); a._ishlnsubmul(b, qj, j); while (a.negative !== 0) { qj--; a.negative = 0; a._ishlnsubmul(b, 1, j); if (!a.isZero()) { a.negative ^= 1; } } if (q) { q.words[j] = qj; } } if (q) { q._strip(); } a._strip(); // Denormalize if (mode !== 'div' && shift !== 0) { a.iushrn(shift); } return { div: q || null, mod: a }; }; // NOTE: 1) `mode` can be set to `mod` to request mod only, // to `div` to request div only, or be absent to // request both div & mod // 2) `positive` is true if unsigned mod is requested BN.prototype.divmod = function divmod (num, mode, positive) { assert(!num.isZero()); if (this.isZero()) { return { div: new BN(0), mod: new BN(0) }; } var div, mod, res; if (this.negative !== 0 && num.negative === 0) { res = this.neg().divmod(num, mode); if (mode !== 'mod') { div = res.div.neg(); } if (mode !== 'div') { mod = res.mod.neg(); if (positive && mod.negative !== 0) { mod.iadd(num); } } return { div: div, mod: mod }; } if (this.negative === 0 && num.negative !== 0) { res = this.divmod(num.neg(), mode); if (mode !== 'mod') { div = res.div.neg(); } return { div: div, mod: res.mod }; } if ((this.negative & num.negative) !== 0) { res = this.neg().divmod(num.neg(), mode); if (mode !== 'div') { mod = res.mod.neg(); if (positive && mod.negative !== 0) { mod.isub(num); } } return { div: res.div, mod: mod }; } // Both numbers are positive at this point // Strip both numbers to approximate shift value if (num.length > this.length || this.cmp(num) < 0) { return { div: new BN(0), mod: this }; } // Very short reduction if (num.length === 1) { if (mode === 'div') { return { div: this.divn(num.words[0]), mod: null }; } if (mode === 'mod') { return { div: null, mod: new BN(this.modrn(num.words[0])) }; } return { div: this.divn(num.words[0]), mod: new BN(this.modrn(num.words[0])) }; } return this._wordDiv(num, mode); }; // Find `this` / `num` BN.prototype.div = function div (num) { return this.divmod(num, 'div', false).div; }; // Find `this` % `num` BN.prototype.mod = function mod (num) { return this.divmod(num, 'mod', false).mod; }; BN.prototype.umod = function umod (num) { return this.divmod(num, 'mod', true).mod; }; // Find Round(`this` / `num`) BN.prototype.divRound = function divRound (num) { var dm = this.divmod(num); // Fast case - exact division if (dm.mod.isZero()) return dm.div; var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; var half = num.ushrn(1); var r2 = num.andln(1); var cmp = mod.cmp(half); // Round down if (cmp < 0 || (r2 === 1 && cmp === 0)) return dm.div; // Round up return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); }; BN.prototype.modrn = function modrn (num) { var isNegNum = num < 0; if (isNegNum) num = -num; assert(num <= 0x3ffffff); var p = (1 << 26) % num; var acc = 0; for (var i = this.length - 1; i >= 0; i--) { acc = (p * acc + (this.words[i] | 0)) % num; } return isNegNum ? -acc : acc; }; // WARNING: DEPRECATED BN.prototype.modn = function modn (num) { return this.modrn(num); }; // In-place division by number BN.prototype.idivn = function idivn (num) { var isNegNum = num < 0; if (isNegNum) num = -num; assert(num <= 0x3ffffff); var carry = 0; for (var i = this.length - 1; i >= 0; i--) { var w = (this.words[i] | 0) + carry * 0x4000000; this.words[i] = (w / num) | 0; carry = w % num; } this._strip(); return isNegNum ? this.ineg() : this; }; BN.prototype.divn = function divn (num) { return this.clone().idivn(num); }; BN.prototype.egcd = function egcd (p) { assert(p.negative === 0); assert(!p.isZero()); var x = this; var y = p.clone(); if (x.negative !== 0) { x = x.umod(p); } else { x = x.clone(); } // A * x + B * y = x var A = new BN(1); var B = new BN(0); // C * x + D * y = y var C = new BN(0); var D = new BN(1); var g = 0; while (x.isEven() && y.isEven()) { x.iushrn(1); y.iushrn(1); ++g; } var yp = y.clone(); var xp = x.clone(); while (!x.isZero()) { for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); if (i > 0) { x.iushrn(i); while (i-- > 0) { if (A.isOdd() || B.isOdd()) { A.iadd(yp); B.isub(xp); } A.iushrn(1); B.iushrn(1); } } for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); if (j > 0) { y.iushrn(j); while (j-- > 0) { if (C.isOdd() || D.isOdd()) { C.iadd(yp); D.isub(xp); } C.iushrn(1); D.iushrn(1); } } if (x.cmp(y) >= 0) { x.isub(y); A.isub(C); B.isub(D); } else { y.isub(x); C.isub(A); D.isub(B); } } return { a: C, b: D, gcd: y.iushln(g) }; }; // This is reduced incarnation of the binary EEA // above, designated to invert members of the // _prime_ fields F(p) at a maximal speed BN.prototype._invmp = function _invmp (p) { assert(p.negative === 0); assert(!p.isZero()); var a = this; var b = p.clone(); if (a.negative !== 0) { a = a.umod(p); } else { a = a.clone(); } var x1 = new BN(1); var x2 = new BN(0); var delta = b.clone(); while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); if (i > 0) { a.iushrn(i); while (i-- > 0) { if (x1.isOdd()) { x1.iadd(delta); } x1.iushrn(1); } } for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); if (j > 0) { b.iushrn(j); while (j-- > 0) { if (x2.isOdd()) { x2.iadd(delta); } x2.iushrn(1); } } if (a.cmp(b) >= 0) { a.isub(b); x1.isub(x2); } else { b.isub(a); x2.isub(x1); } } var res; if (a.cmpn(1) === 0) { res = x1; } else { res = x2; } if (res.cmpn(0) < 0) { res.iadd(p); } return res; }; BN.prototype.gcd = function gcd (num) { if (this.isZero()) return num.abs(); if (num.isZero()) return this.abs(); var a = this.clone(); var b = num.clone(); a.negative = 0; b.negative = 0; // Remove common factor of two for (var shift = 0; a.isEven() && b.isEven(); shift++) { a.iushrn(1); b.iushrn(1); } do { while (a.isEven()) { a.iushrn(1); } while (b.isEven()) { b.iushrn(1); } var r = a.cmp(b); if (r < 0) { // Swap `a` and `b` to make `a` always bigger than `b` var t = a; a = b; b = t; } else if (r === 0 || b.cmpn(1) === 0) { break; } a.isub(b); } while (true); return b.iushln(shift); }; // Invert number in the field F(num) BN.prototype.invm = function invm (num) { return this.egcd(num).a.umod(num); }; BN.prototype.isEven = function isEven () { return (this.words[0] & 1) === 0; }; BN.prototype.isOdd = function isOdd () { return (this.words[0] & 1) === 1; }; // And first word and num BN.prototype.andln = function andln (num) { return this.words[0] & num; }; // Increment at the bit position in-line BN.prototype.bincn = function bincn (bit) { assert(typeof bit === 'number'); var r = bit % 26; var s = (bit - r) / 26; var q = 1 << r; // Fast case: bit is much higher than all existing words if (this.length <= s) { this._expand(s + 1); this.words[s] |= q; return this; } // Add bit and propagate, if needed var carry = q; for (var i = s; carry !== 0 && i < this.length; i++) { var w = this.words[i] | 0; w += carry; carry = w >>> 26; w &= 0x3ffffff; this.words[i] = w; } if (carry !== 0) { this.words[i] = carry; this.length++; } return this; }; BN.prototype.isZero = function isZero () { return this.length === 1 && this.words[0] === 0; }; BN.prototype.cmpn = function cmpn (num) { var negative = num < 0; if (this.negative !== 0 && !negative) return -1; if (this.negative === 0 && negative) return 1; this._strip(); var res; if (this.length > 1) { res = 1; } else { if (negative) { num = -num; } assert(num <= 0x3ffffff, 'Number is too big'); var w = this.words[0] | 0; res = w === num ? 0 : w < num ? -1 : 1; } if (this.negative !== 0) return -res | 0; return res; }; // Compare two numbers and return: // 1 - if `this` > `num` // 0 - if `this` == `num` // -1 - if `this` < `num` BN.prototype.cmp = function cmp (num) { if (this.negative !== 0 && num.negative === 0) return -1; if (this.negative === 0 && num.negative !== 0) return 1; var res = this.ucmp(num); if (this.negative !== 0) return -res | 0; return res; }; // Unsigned comparison BN.prototype.ucmp = function ucmp (num) { // At this point both numbers have the same sign if (this.length > num.length) return 1; if (this.length < num.length) return -1; var res = 0; for (var i = this.length - 1; i >= 0; i--) { var a = this.words[i] | 0; var b = num.words[i] | 0; if (a === b) continue; if (a < b) { res = -1; } else if (a > b) { res = 1; } break; } return res; }; BN.prototype.gtn = function gtn (num) { return this.cmpn(num) === 1; }; BN.prototype.gt = function gt (num) { return this.cmp(num) === 1; }; BN.prototype.gten = function gten (num) { return this.cmpn(num) >= 0; }; BN.prototype.gte = function gte (num) { return this.cmp(num) >= 0; }; BN.prototype.ltn = function ltn (num) { return this.cmpn(num) === -1; }; BN.prototype.lt = function lt (num) { return this.cmp(num) === -1; }; BN.prototype.lten = function lten (num) { return this.cmpn(num) <= 0; }; BN.prototype.lte = function lte (num) { return this.cmp(num) <= 0; }; BN.prototype.eqn = function eqn (num) { return this.cmpn(num) === 0; }; BN.prototype.eq = function eq (num) { return this.cmp(num) === 0; }; // // A reduce context, could be using montgomery or something better, depending // on the `m` itself. // BN.red = function red (num) { return new Red(num); }; BN.prototype.toRed = function toRed (ctx) { assert(!this.red, 'Already a number in reduction context'); assert(this.negative === 0, 'red works only with positives'); return ctx.convertTo(this)._forceRed(ctx); }; BN.prototype.fromRed = function fromRed () { assert(this.red, 'fromRed works only with numbers in reduction context'); return this.red.convertFrom(this); }; BN.prototype._forceRed = function _forceRed (ctx) { this.red = ctx; return this; }; BN.prototype.forceRed = function forceRed (ctx) { assert(!this.red, 'Already a number in reduction context'); return this._forceRed(ctx); }; BN.prototype.redAdd = function redAdd (num) { assert(this.red, 'redAdd works only with red numbers'); return this.red.add(this, num); }; BN.prototype.redIAdd = function redIAdd (num) { assert(this.red, 'redIAdd works only with red numbers'); return this.red.iadd(this, num); }; BN.prototype.redSub = function redSub (num) { assert(this.red, 'redSub works only with red numbers'); return this.red.sub(this, num); }; BN.prototype.redISub = function redISub (num) { assert(this.red, 'redISub works only with red numbers'); return this.red.isub(this, num); }; BN.prototype.redShl = function redShl (num) { assert(this.red, 'redShl works only with red numbers'); return this.red.shl(this, num); }; BN.prototype.redMul = function redMul (num) { assert(this.red, 'redMul works only with red numbers'); this.red._verify2(this, num); return this.red.mul(this, num); }; BN.prototype.redIMul = function redIMul (num) { assert(this.red, 'redMul works only with red numbers'); this.red._verify2(this, num); return this.red.imul(this, num); }; BN.prototype.redSqr = function redSqr () { assert(this.red, 'redSqr works only with red numbers'); this.red._verify1(this); return this.red.sqr(this); }; BN.prototype.redISqr = function redISqr () { assert(this.red, 'redISqr works only with red numbers'); this.red._verify1(this); return this.red.isqr(this); }; // Square root over p BN.prototype.redSqrt = function redSqrt () { assert(this.red, 'redSqrt works only with red numbers'); this.red._verify1(this); return this.red.sqrt(this); }; BN.prototype.redInvm = function redInvm () { assert(this.red, 'redInvm works only with red numbers'); this.red._verify1(this); return this.red.invm(this); }; // Return negative clone of `this` % `red modulo` BN.prototype.redNeg = function redNeg () { assert(this.red, 'redNeg works only with red numbers'); this.red._verify1(this); return this.red.neg(this); }; BN.prototype.redPow = function redPow (num) { assert(this.red && !num.red, 'redPow(normalNum)'); this.red._verify1(this); return this.red.pow(this, num); }; // Prime numbers with efficient reduction var primes = { k256: null, p224: null, p192: null, p25519: null }; // Pseudo-Mersenne prime function MPrime (name, p) { // P = 2 ^ N - K this.name = name; this.p = new BN(p, 16); this.n = this.p.bitLength(); this.k = new BN(1).iushln(this.n).isub(this.p); this.tmp = this._tmp(); } MPrime.prototype._tmp = function _tmp () { var tmp = new BN(null); tmp.words = new Array(Math.ceil(this.n / 13)); return tmp; }; MPrime.prototype.ireduce = function ireduce (num) { // Assumes that `num` is less than `P^2` // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) var r = num; var rlen; do { this.split(r, this.tmp); r = this.imulK(r); r = r.iadd(this.tmp); rlen = r.bitLength(); } while (rlen > this.n); var cmp = rlen < this.n ? -1 : r.ucmp(this.p); if (cmp === 0) { r.words[0] = 0; r.length = 1; } else if (cmp > 0) { r.isub(this.p); } else { r._strip(); } return r; }; MPrime.prototype.split = function split (input, out) { input.iushrn(this.n, 0, out); }; MPrime.prototype.imulK = function imulK (num) { return num.imul(this.k); }; function K256 () { MPrime.call( this, 'k256', 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); } inherits(K256, MPrime); K256.prototype.split = function split (input, output) { // 256 = 9 * 26 + 22 var mask = 0x3fffff; var outLen = Math.min(input.length, 9); for (var i = 0; i < outLen; i++) { output.words[i] = input.words[i]; } output.length = outLen; if (input.length <= 9) { input.words[0] = 0; input.length = 1; return; } // Shift by 9 limbs var prev = input.words[9]; output.words[output.length++] = prev & mask; for (i = 10; i < input.length; i++) { var next = input.words[i] | 0; input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); prev = next; } prev >>>= 22; input.words[i - 10] = prev; if (prev === 0 && input.length > 10) { input.length -= 10; } else { input.length -= 9; } }; K256.prototype.imulK = function imulK (num) { // K = 0x1000003d1 = [ 0x40, 0x3d1 ] num.words[num.length] = 0; num.words[num.length + 1] = 0; num.length += 2; // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 var lo = 0; for (var i = 0; i < num.length; i++) { var w = num.words[i] | 0; lo += w * 0x3d1; num.words[i] = lo & 0x3ffffff; lo = w * 0x40 + ((lo / 0x4000000) | 0); } // Fast length reduction if (num.words[num.length - 1] === 0) { num.length--; if (num.words[num.length - 1] === 0) { num.length--; } } return num; }; function P224 () { MPrime.call( this, 'p224', 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); } inherits(P224, MPrime); function P192 () { MPrime.call( this, 'p192', 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); } inherits(P192, MPrime); function P25519 () { // 2 ^ 255 - 19 MPrime.call( this, '25519', '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); } inherits(P25519, MPrime); P25519.prototype.imulK = function imulK (num) { // K = 0x13 var carry = 0; for (var i = 0; i < num.length; i++) { var hi = (num.words[i] | 0) * 0x13 + carry; var lo = hi & 0x3ffffff; hi >>>= 26; num.words[i] = lo; carry = hi; } if (carry !== 0) { num.words[num.length++] = carry; } return num; }; // Exported mostly for testing purposes, use plain name instead BN._prime = function prime (name) { // Cached version of prime if (primes[name]) return primes[name]; var prime; if (name === 'k256') { prime = new K256(); } else if (name === 'p224') { prime = new P224(); } else if (name === 'p192') { prime = new P192(); } else if (name === 'p25519') { prime = new P25519(); } else { throw new Error('Unknown prime ' + name); } primes[name] = prime; return prime; }; // // Base reduction engine // function Red (m) { if (typeof m === 'string') { var prime = BN._prime(m); this.m = prime.p; this.prime = prime; } else { assert(m.gtn(1), 'modulus must be greater than 1'); this.m = m; this.prime = null; } } Red.prototype._verify1 = function _verify1 (a) { assert(a.negative === 0, 'red works only with positives'); assert(a.red, 'red works only with red numbers'); }; Red.prototype._verify2 = function _verify2 (a, b) { assert((a.negative | b.negative) === 0, 'red works only with positives'); assert(a.red && a.red === b.red, 'red works only with red numbers'); }; Red.prototype.imod = function imod (a) { if (this.prime) return this.prime.ireduce(a)._forceRed(this); a.umod(this.m)._forceRed(this)._move(a); return a; }; Red.prototype.neg = function neg (a) { if (a.isZero()) { return a.clone(); } return this.m.sub(a)._forceRed(this); }; Red.prototype.add = function add (a, b) { this._verify2(a, b); var res = a.add(b); if (res.cmp(this.m) >= 0) { res.isub(this.m); } return res._forceRed(this); }; Red.prototype.iadd = function iadd (a, b) { this._verify2(a, b); var res = a.iadd(b); if (res.cmp(this.m) >= 0) { res.isub(this.m); } return res; }; Red.prototype.sub = function sub (a, b) { this._verify2(a, b); var res = a.sub(b); if (res.cmpn(0) < 0) { res.iadd(this.m); } return res._forceRed(this); }; Red.prototype.isub = function isub (a, b) { this._verify2(a, b); var res = a.isub(b); if (res.cmpn(0) < 0) { res.iadd(this.m); } return res; }; Red.prototype.shl = function shl (a, num) { this._verify1(a); return this.imod(a.ushln(num)); }; Red.prototype.imul = function imul (a, b) { this._verify2(a, b); return this.imod(a.imul(b)); }; Red.prototype.mul = function mul (a, b) { this._verify2(a, b); return this.imod(a.mul(b)); }; Red.prototype.isqr = function isqr (a) { return this.imul(a, a.clone()); }; Red.prototype.sqr = function sqr (a) { return this.mul(a, a); }; Red.prototype.sqrt = function sqrt (a) { if (a.isZero()) return a.clone(); var mod3 = this.m.andln(3); assert(mod3 % 2 === 1); // Fast case if (mod3 === 3) { var pow = this.m.add(new BN(1)).iushrn(2); return this.pow(a, pow); } // Tonelli-Shanks algorithm (Totally unoptimized and slow) // // Find Q and S, that Q * 2 ^ S = (P - 1) var q = this.m.subn(1); var s = 0; while (!q.isZero() && q.andln(1) === 0) { s++; q.iushrn(1); } assert(!q.isZero()); var one = new BN(1).toRed(this); var nOne = one.redNeg(); // Find quadratic non-residue // NOTE: Max is such because of generalized Riemann hypothesis. var lpow = this.m.subn(1).iushrn(1); var z = this.m.bitLength(); z = new BN(2 * z * z).toRed(this); while (this.pow(z, lpow).cmp(nOne) !== 0) { z.redIAdd(nOne); } var c = this.pow(z, q); var r = this.pow(a, q.addn(1).iushrn(1)); var t = this.pow(a, q); var m = s; while (t.cmp(one) !== 0) { var tmp = t; for (var i = 0; tmp.cmp(one) !== 0; i++) { tmp = tmp.redSqr(); } assert(i < m); var b = this.pow(c, new BN(1).iushln(m - i - 1)); r = r.redMul(b); c = b.redSqr(); t = t.redMul(c); m = i; } return r; }; Red.prototype.invm = function invm (a) { var inv = a._invmp(this.m); if (inv.negative !== 0) { inv.negative = 0; return this.imod(inv).redNeg(); } else { return this.imod(inv); } }; Red.prototype.pow = function pow (a, num) { if (num.isZero()) return new BN(1).toRed(this); if (num.cmpn(1) === 0) return a.clone(); var windowSize = 4; var wnd = new Array(1 << windowSize); wnd[0] = new BN(1).toRed(this); wnd[1] = a; for (var i = 2; i < wnd.length; i++) { wnd[i] = this.mul(wnd[i - 1], a); } var res = wnd[0]; var current = 0; var currentLen = 0; var start = num.bitLength() % 26; if (start === 0) { start = 26; } for (i = num.length - 1; i >= 0; i--) { var word = num.words[i]; for (var j = start - 1; j >= 0; j--) { var bit = (word >> j) & 1; if (res !== wnd[0]) { res = this.sqr(res); } if (bit === 0 && current === 0) { currentLen = 0; continue; } current <<= 1; current |= bit; currentLen++; if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; res = this.mul(res, wnd[current]); currentLen = 0; current = 0; } start = 26; } return res; }; Red.prototype.convertTo = function convertTo (num) { var r = num.umod(this.m); return r === num ? r.clone() : r; }; Red.prototype.convertFrom = function convertFrom (num) { var res = num.clone(); res.red = null; return res; }; // // Montgomery method engine // BN.mont = function mont (num) { return new Mont(num); }; function Mont (m) { Red.call(this, m); this.shift = this.m.bitLength(); if (this.shift % 26 !== 0) { this.shift += 26 - (this.shift % 26); } this.r = new BN(1).iushln(this.shift); this.r2 = this.imod(this.r.sqr()); this.rinv = this.r._invmp(this.m); this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); this.minv = this.minv.umod(this.r); this.minv = this.r.sub(this.minv); } inherits(Mont, Red); Mont.prototype.convertTo = function convertTo (num) { return this.imod(num.ushln(this.shift)); }; Mont.prototype.convertFrom = function convertFrom (num) { var r = this.imod(num.mul(this.rinv)); r.red = null; return r; }; Mont.prototype.imul = function imul (a, b) { if (a.isZero() || b.isZero()) { a.words[0] = 0; a.length = 1; return a; } var t = a.imul(b); var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); var u = t.isub(c).iushrn(this.shift); var res = u; if (u.cmp(this.m) >= 0) { res = u.isub(this.m); } else if (u.cmpn(0) < 0) { res = u.iadd(this.m); } return res._forceRed(this); }; Mont.prototype.mul = function mul (a, b) { if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); var t = a.mul(b); var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); var u = t.isub(c).iushrn(this.shift); var res = u; if (u.cmp(this.m) >= 0) { res = u.isub(this.m); } else if (u.cmpn(0) < 0) { res = u.iadd(this.m); } return res._forceRed(this); }; Mont.prototype.invm = function invm (a) { // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R var res = this.imod(a._invmp(this.m).mul(this.r2)); return res._forceRed(this); }; })(typeof module === 'undefined' || module, this); },{"buffer":178}],120:[function(require,module,exports){ ;(function (root, factory) { if (typeof exports === "object") { // CommonJS module.exports = exports = factory(); } else if (typeof define === "function" && define.amd) { // AMD define([], factory); } else { // Global (browser) root.CryptoJS = factory(); } }(this, function () { /** * CryptoJS core components. */ var CryptoJS = CryptoJS || (function (Math, undefined) { /* * Local polyfil of Object.create */ var create = Object.create || (function () { function F() {}; return function (obj) { var subtype; F.prototype = obj; subtype = new F(); F.prototype = null; return subtype; }; }()) /** * CryptoJS namespace. */ var C = {}; /** * Library namespace. */ var C_lib = C.lib = {}; /** * Base object for prototypal inheritance. */ var Base = C_lib.Base = (function () { return { /** * Creates a new object that inherits from this object. * * @param {Object} overrides Properties to copy into the new object. * * @return {Object} The new object. * * @static * * @example * * var MyType = CryptoJS.lib.Base.extend({ * field: 'value', * * method: function () { * } * }); */ extend: function (overrides) { // Spawn var subtype = create(this); // Augment if (overrides) { subtype.mixIn(overrides); } // Create default initializer if (!subtype.hasOwnProperty('init') || this.init === subtype.init) { subtype.init = function () { subtype.$super.init.apply(this, arguments); }; } // Initializer's prototype is the subtype object subtype.init.prototype = subtype; // Reference supertype subtype.$super = this; return subtype; }, /** * Extends this object and runs the init method. * Arguments to create() will be passed to init(). * * @return {Object} The new object. * * @static * * @example * * var instance = MyType.create(); */ create: function () { var instance = this.extend(); instance.init.apply(instance, arguments); return instance; }, /** * Initializes a newly created object. * Override this method to add some logic when your objects are created. * * @example * * var MyType = CryptoJS.lib.Base.extend({ * init: function () { * // ... * } * }); */ init: function () { }, /** * Copies properties into this object. * * @param {Object} properties The properties to mix in. * * @example * * MyType.mixIn({ * field: 'value' * }); */ mixIn: function (properties) { for (var propertyName in properties) { if (properties.hasOwnProperty(propertyName)) { this[propertyName] = properties[propertyName]; } } // IE won't copy toString using the loop above if (properties.hasOwnProperty('toString')) { this.toString = properties.toString; } }, /** * Creates a copy of this object. * * @return {Object} The clone. * * @example * * var clone = instance.clone(); */ clone: function () { return this.init.prototype.extend(this); } }; }()); /** * An array of 32-bit words. * * @property {Array} words The array of 32-bit words. * @property {number} sigBytes The number of significant bytes in this word array. */ var WordArray = C_lib.WordArray = Base.extend({ /** * Initializes a newly created word array. * * @param {Array} words (Optional) An array of 32-bit words. * @param {number} sigBytes (Optional) The number of significant bytes in the words. * * @example * * var wordArray = CryptoJS.lib.WordArray.create(); * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); */ init: function (words, sigBytes) { words = this.words = words || []; if (sigBytes != undefined) { this.sigBytes = sigBytes; } else { this.sigBytes = words.length * 4; } }, /** * Converts this word array to a string. * * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex * * @return {string} The stringified word array. * * @example * * var string = wordArray + ''; * var string = wordArray.toString(); * var string = wordArray.toString(CryptoJS.enc.Utf8); */ toString: function (encoder) { return (encoder || Hex).stringify(this); }, /** * Concatenates a word array to this word array. * * @param {WordArray} wordArray The word array to append. * * @return {WordArray} This word array. * * @example * * wordArray1.concat(wordArray2); */ concat: function (wordArray) { // Shortcuts var thisWords = this.words; var thatWords = wordArray.words; var thisSigBytes = this.sigBytes; var thatSigBytes = wordArray.sigBytes; // Clamp excess bits this.clamp(); // Concat if (thisSigBytes % 4) { // Copy one byte at a time for (var i = 0; i < thatSigBytes; i++) { var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); } } else { // Copy one word at a time for (var i = 0; i < thatSigBytes; i += 4) { thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; } } this.sigBytes += thatSigBytes; // Chainable return this; }, /** * Removes insignificant bits. * * @example * * wordArray.clamp(); */ clamp: function () { // Shortcuts var words = this.words; var sigBytes = this.sigBytes; // Clamp words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); words.length = Math.ceil(sigBytes / 4); }, /** * Creates a copy of this word array. * * @return {WordArray} The clone. * * @example * * var clone = wordArray.clone(); */ clone: function () { var clone = Base.clone.call(this); clone.words = this.words.slice(0); return clone; }, /** * Creates a word array filled with random bytes. * * @param {number} nBytes The number of random bytes to generate. * * @return {WordArray} The random word array. * * @static * * @example * * var wordArray = CryptoJS.lib.WordArray.random(16); */ random: function (nBytes) { var words = []; var r = (function (m_w) { var m_w = m_w; var m_z = 0x3ade68b1; var mask = 0xffffffff; return function () { m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; var result = ((m_z << 0x10) + m_w) & mask; result /= 0x100000000; result += 0.5; return result * (Math.random() > .5 ? 1 : -1); } }); for (var i = 0, rcache; i < nBytes; i += 4) { var _r = r((rcache || Math.random()) * 0x100000000); rcache = _r() * 0x3ade67b7; words.push((_r() * 0x100000000) | 0); } return new WordArray.init(words, nBytes); } }); /** * Encoder namespace. */ var C_enc = C.enc = {}; /** * Hex encoding strategy. */ var Hex = C_enc.Hex = { /** * Converts a word array to a hex string. * * @param {WordArray} wordArray The word array. * * @return {string} The hex string. * * @static * * @example * * var hexString = CryptoJS.enc.Hex.stringify(wordArray); */ stringify: function (wordArray) { // Shortcuts var words = wordArray.words; var sigBytes = wordArray.sigBytes; // Convert var hexChars = []; for (var i = 0; i < sigBytes; i++) { var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; hexChars.push((bite >>> 4).toString(16)); hexChars.push((bite & 0x0f).toString(16)); } return hexChars.join(''); }, /** * Converts a hex string to a word array. * * @param {string} hexStr The hex string. * * @return {WordArray} The word array. * * @static * * @example * * var wordArray = CryptoJS.enc.Hex.parse(hexString); */ parse: function (hexStr) { // Shortcut var hexStrLength = hexStr.length; // Convert var words = []; for (var i = 0; i < hexStrLength; i += 2) { words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); } return new WordArray.init(words, hexStrLength / 2); } }; /** * Latin1 encoding strategy. */ var Latin1 = C_enc.Latin1 = { /** * Converts a word array to a Latin1 string. * * @param {WordArray} wordArray The word array. * * @return {string} The Latin1 string. * * @static * * @example * * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); */ stringify: function (wordArray) { // Shortcuts var words = wordArray.words; var sigBytes = wordArray.sigBytes; // Convert var latin1Chars = []; for (var i = 0; i < sigBytes; i++) { var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; latin1Chars.push(String.fromCharCode(bite)); } return latin1Chars.join(''); }, /** * Converts a Latin1 string to a word array. * * @param {string} latin1Str The Latin1 string. * * @return {WordArray} The word array. * * @static * * @example * * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); */ parse: function (latin1Str) { // Shortcut var latin1StrLength = latin1Str.length; // Convert var words = []; for (var i = 0; i < latin1StrLength; i++) { words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); } return new WordArray.init(words, latin1StrLength); } }; /** * UTF-8 encoding strategy. */ var Utf8 = C_enc.Utf8 = { /** * Converts a word array to a UTF-8 string. * * @param {WordArray} wordArray The word array. * * @return {string} The UTF-8 string. * * @static * * @example * * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); */ stringify: function (wordArray) { try { return decodeURIComponent(escape(Latin1.stringify(wordArray))); } catch (e) { throw new Error('Malformed UTF-8 data'); } }, /** * Converts a UTF-8 string to a word array. * * @param {string} utf8Str The UTF-8 string. * * @return {WordArray} The word array. * * @static * * @example * * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); */ parse: function (utf8Str) { return Latin1.parse(unescape(encodeURIComponent(utf8Str))); } }; /** * Abstract buffered block algorithm template. * * The property blockSize must be implemented in a concrete subtype. * * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 */ var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ /** * Resets this block algorithm's data buffer to its initial state. * * @example * * bufferedBlockAlgorithm.reset(); */ reset: function () { // Initial values this._data = new WordArray.init(); this._nDataBytes = 0; }, /** * Adds new data to this block algorithm's buffer. * * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. * * @example * * bufferedBlockAlgorithm._append('data'); * bufferedBlockAlgorithm._append(wordArray); */ _append: function (data) { // Convert string to WordArray, else assume WordArray already if (typeof data == 'string') { data = Utf8.parse(data); } // Append this._data.concat(data); this._nDataBytes += data.sigBytes; }, /** * Processes available data blocks. * * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. * * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. * * @return {WordArray} The processed data. * * @example * * var processedData = bufferedBlockAlgorithm._process(); * var processedData = bufferedBlockAlgorithm._process(!!'flush'); */ _process: function (doFlush) { // Shortcuts var data = this._data; var dataWords = data.words; var dataSigBytes = data.sigBytes; var blockSize = this.blockSize; var blockSizeBytes = blockSize * 4; // Count blocks ready var nBlocksReady = dataSigBytes / blockSizeBytes; if (doFlush) { // Round up to include partial blocks nBlocksReady = Math.ceil(nBlocksReady); } else { // Round down to include only full blocks, // less the number of blocks that must remain in the buffer nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); } // Count words ready var nWordsReady = nBlocksReady * blockSize; // Count bytes ready var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); // Process blocks if (nWordsReady) { for (var offset = 0; offset < nWordsReady; offset += blockSize) { // Perform concrete-algorithm logic this._doProcessBlock(dataWords, offset); } // Remove processed words var processedWords = dataWords.splice(0, nWordsReady); data.sigBytes -= nBytesReady; } // Return processed words return new WordArray.init(processedWords, nBytesReady); }, /** * Creates a copy of this object. * * @return {Object} The clone. * * @example * * var clone = bufferedBlockAlgorithm.clone(); */ clone: function () { var clone = Base.clone.call(this); clone._data = this._data.clone(); return clone; }, _minBufferSize: 0 }); /** * Abstract hasher template. * * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) */ var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ /** * Configuration options. */ cfg: Base.extend(), /** * Initializes a newly created hasher. * * @param {Object} cfg (Optional) The configuration options to use for this hash computation. * * @example * * var hasher = CryptoJS.algo.SHA256.create(); */ init: function (cfg) { // Apply config defaults this.cfg = this.cfg.extend(cfg); // Set initial values this.reset(); }, /** * Resets this hasher to its initial state. * * @example * * hasher.reset(); */ reset: function () { // Reset data buffer BufferedBlockAlgorithm.reset.call(this); // Perform concrete-hasher logic this._doReset(); }, /** * Updates this hasher with a message. * * @param {WordArray|string} messageUpdate The message to append. * * @return {Hasher} This hasher. * * @example * * hasher.update('message'); * hasher.update(wordArray); */ update: function (messageUpdate) { // Append this._append(messageUpdate); // Update the hash this._process(); // Chainable return this; }, /** * Finalizes the hash computation. * Note that the finalize operation is effectively a destructive, read-once operation. * * @param {WordArray|string} messageUpdate (Optional) A final message update. * * @return {WordArray} The hash. * * @example * * var hash = hasher.finalize(); * var hash = hasher.finalize('message'); * var hash = hasher.finalize(wordArray); */ finalize: function (messageUpdate) { // Final message update if (messageUpdate) { this._append(messageUpdate); } // Perform concrete-hasher logic var hash = this._doFinalize(); return hash; }, blockSize: 512/32, /** * Creates a shortcut function to a hasher's object interface. * * @param {Hasher} hasher The hasher to create a helper for. * * @return {Function} The shortcut function. * * @static * * @example * * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); */ _createHelper: function (hasher) { return function (message, cfg) { return new hasher.init(cfg).finalize(message); }; }, /** * Creates a shortcut function to the HMAC's object interface. * * @param {Hasher} hasher The hasher to use in this HMAC helper. * * @return {Function} The shortcut function. * * @static * * @example * * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); */ _createHmacHelper: function (hasher) { return function (message, key) { return new C_algo.HMAC.init(hasher, key).finalize(message); }; } }); /** * Algorithm namespace. */ var C_algo = C.algo = {}; return C; }(Math)); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var C_enc = C.enc; /** * Base64 encoding strategy. */ var Base64 = C_enc.Base64 = { /** * Converts a word array to a Base64 string. * * @param {WordArray} wordArray The word array. * * @return {string} The Base64 string. * * @static * * @example * * var base64String = CryptoJS.enc.Base64.stringify(wordArray); */ stringify: function (wordArray) { // Shortcuts var words = wordArray.words; var sigBytes = wordArray.sigBytes; var map = this._map; // Clamp excess bits wordArray.clamp(); // Convert var base64Chars = []; for (var i = 0; i < sigBytes; i += 3) { var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; var triplet = (byte1 << 16) | (byte2 << 8) | byte3; for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); } } // Add padding var paddingChar = map.charAt(64); if (paddingChar) { while (base64Chars.length % 4) { base64Chars.push(paddingChar); } } return base64Chars.join(''); }, /** * Converts a Base64 string to a word array. * * @param {string} base64Str The Base64 string. * * @return {WordArray} The word array. * * @static * * @example * * var wordArray = CryptoJS.enc.Base64.parse(base64String); */ parse: function (base64Str) { // Shortcuts var base64StrLength = base64Str.length; var map = this._map; var reverseMap = this._reverseMap; if (!reverseMap) { reverseMap = this._reverseMap = []; for (var j = 0; j < map.length; j++) { reverseMap[map.charCodeAt(j)] = j; } } // Ignore padding var paddingChar = map.charAt(64); if (paddingChar) { var paddingIndex = base64Str.indexOf(paddingChar); if (paddingIndex !== -1) { base64StrLength = paddingIndex; } } // Convert return parseLoop(base64Str, base64StrLength, reverseMap); }, _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' }; function parseLoop(base64Str, base64StrLength, reverseMap) { var words = []; var nBytes = 0; for (var i = 0; i < base64StrLength; i++) { if (i % 4) { var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2); var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2); words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); nBytes++; } } return WordArray.create(words, nBytes); } }()); (function (Math) { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var Hasher = C_lib.Hasher; var C_algo = C.algo; // Constants table var T = []; // Compute constants (function () { for (var i = 0; i < 64; i++) { T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0; } }()); /** * MD5 hash algorithm. */ var MD5 = C_algo.MD5 = Hasher.extend({ _doReset: function () { this._hash = new WordArray.init([ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 ]); }, _doProcessBlock: function (M, offset) { // Swap endian for (var i = 0; i < 16; i++) { // Shortcuts var offset_i = offset + i; var M_offset_i = M[offset_i]; M[offset_i] = ( (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) ); } // Shortcuts var H = this._hash.words; var M_offset_0 = M[offset + 0]; var M_offset_1 = M[offset + 1]; var M_offset_2 = M[offset + 2]; var M_offset_3 = M[offset + 3]; var M_offset_4 = M[offset + 4]; var M_offset_5 = M[offset + 5]; var M_offset_6 = M[offset + 6]; var M_offset_7 = M[offset + 7]; var M_offset_8 = M[offset + 8]; var M_offset_9 = M[offset + 9]; var M_offset_10 = M[offset + 10]; var M_offset_11 = M[offset + 11]; var M_offset_12 = M[offset + 12]; var M_offset_13 = M[offset + 13]; var M_offset_14 = M[offset + 14]; var M_offset_15 = M[offset + 15]; // Working varialbes var a = H[0]; var b = H[1]; var c = H[2]; var d = H[3]; // Computation a = FF(a, b, c, d, M_offset_0, 7, T[0]); d = FF(d, a, b, c, M_offset_1, 12, T[1]); c = FF(c, d, a, b, M_offset_2, 17, T[2]); b = FF(b, c, d, a, M_offset_3, 22, T[3]); a = FF(a, b, c, d, M_offset_4, 7, T[4]); d = FF(d, a, b, c, M_offset_5, 12, T[5]); c = FF(c, d, a, b, M_offset_6, 17, T[6]); b = FF(b, c, d, a, M_offset_7, 22, T[7]); a = FF(a, b, c, d, M_offset_8, 7, T[8]); d = FF(d, a, b, c, M_offset_9, 12, T[9]); c = FF(c, d, a, b, M_offset_10, 17, T[10]); b = FF(b, c, d, a, M_offset_11, 22, T[11]); a = FF(a, b, c, d, M_offset_12, 7, T[12]); d = FF(d, a, b, c, M_offset_13, 12, T[13]); c = FF(c, d, a, b, M_offset_14, 17, T[14]); b = FF(b, c, d, a, M_offset_15, 22, T[15]); a = GG(a, b, c, d, M_offset_1, 5, T[16]); d = GG(d, a, b, c, M_offset_6, 9, T[17]); c = GG(c, d, a, b, M_offset_11, 14, T[18]); b = GG(b, c, d, a, M_offset_0, 20, T[19]); a = GG(a, b, c, d, M_offset_5, 5, T[20]); d = GG(d, a, b, c, M_offset_10, 9, T[21]); c = GG(c, d, a, b, M_offset_15, 14, T[22]); b = GG(b, c, d, a, M_offset_4, 20, T[23]); a = GG(a, b, c, d, M_offset_9, 5, T[24]); d = GG(d, a, b, c, M_offset_14, 9, T[25]); c = GG(c, d, a, b, M_offset_3, 14, T[26]); b = GG(b, c, d, a, M_offset_8, 20, T[27]); a = GG(a, b, c, d, M_offset_13, 5, T[28]); d = GG(d, a, b, c, M_offset_2, 9, T[29]); c = GG(c, d, a, b, M_offset_7, 14, T[30]); b = GG(b, c, d, a, M_offset_12, 20, T[31]); a = HH(a, b, c, d, M_offset_5, 4, T[32]); d = HH(d, a, b, c, M_offset_8, 11, T[33]); c = HH(c, d, a, b, M_offset_11, 16, T[34]); b = HH(b, c, d, a, M_offset_14, 23, T[35]); a = HH(a, b, c, d, M_offset_1, 4, T[36]); d = HH(d, a, b, c, M_offset_4, 11, T[37]); c = HH(c, d, a, b, M_offset_7, 16, T[38]); b = HH(b, c, d, a, M_offset_10, 23, T[39]); a = HH(a, b, c, d, M_offset_13, 4, T[40]); d = HH(d, a, b, c, M_offset_0, 11, T[41]); c = HH(c, d, a, b, M_offset_3, 16, T[42]); b = HH(b, c, d, a, M_offset_6, 23, T[43]); a = HH(a, b, c, d, M_offset_9, 4, T[44]); d = HH(d, a, b, c, M_offset_12, 11, T[45]); c = HH(c, d, a, b, M_offset_15, 16, T[46]); b = HH(b, c, d, a, M_offset_2, 23, T[47]); a = II(a, b, c, d, M_offset_0, 6, T[48]); d = II(d, a, b, c, M_offset_7, 10, T[49]); c = II(c, d, a, b, M_offset_14, 15, T[50]); b = II(b, c, d, a, M_offset_5, 21, T[51]); a = II(a, b, c, d, M_offset_12, 6, T[52]); d = II(d, a, b, c, M_offset_3, 10, T[53]); c = II(c, d, a, b, M_offset_10, 15, T[54]); b = II(b, c, d, a, M_offset_1, 21, T[55]); a = II(a, b, c, d, M_offset_8, 6, T[56]); d = II(d, a, b, c, M_offset_15, 10, T[57]); c = II(c, d, a, b, M_offset_6, 15, T[58]); b = II(b, c, d, a, M_offset_13, 21, T[59]); a = II(a, b, c, d, M_offset_4, 6, T[60]); d = II(d, a, b, c, M_offset_11, 10, T[61]); c = II(c, d, a, b, M_offset_2, 15, T[62]); b = II(b, c, d, a, M_offset_9, 21, T[63]); // Intermediate hash value H[0] = (H[0] + a) | 0; H[1] = (H[1] + b) | 0; H[2] = (H[2] + c) | 0; H[3] = (H[3] + d) | 0; }, _doFinalize: function () { // Shortcuts var data = this._data; var dataWords = data.words; var nBitsTotal = this._nDataBytes * 8; var nBitsLeft = data.sigBytes * 8; // Add padding dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000); var nBitsTotalL = nBitsTotal; dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = ( (((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) | (((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00) ); dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( (((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) | (((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00) ); data.sigBytes = (dataWords.length + 1) * 4; // Hash final blocks this._process(); // Shortcuts var hash = this._hash; var H = hash.words; // Swap endian for (var i = 0; i < 4; i++) { // Shortcut var H_i = H[i]; H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); } // Return final computed hash return hash; }, clone: function () { var clone = Hasher.clone.call(this); clone._hash = this._hash.clone(); return clone; } }); function FF(a, b, c, d, x, s, t) { var n = a + ((b & c) | (~b & d)) + x + t; return ((n << s) | (n >>> (32 - s))) + b; } function GG(a, b, c, d, x, s, t) { var n = a + ((b & d) | (c & ~d)) + x + t; return ((n << s) | (n >>> (32 - s))) + b; } function HH(a, b, c, d, x, s, t) { var n = a + (b ^ c ^ d) + x + t; return ((n << s) | (n >>> (32 - s))) + b; } function II(a, b, c, d, x, s, t) { var n = a + (c ^ (b | ~d)) + x + t; return ((n << s) | (n >>> (32 - s))) + b; } /** * Shortcut function to the hasher's object interface. * * @param {WordArray|string} message The message to hash. * * @return {WordArray} The hash. * * @static * * @example * * var hash = CryptoJS.MD5('message'); * var hash = CryptoJS.MD5(wordArray); */ C.MD5 = Hasher._createHelper(MD5); /** * Shortcut function to the HMAC's object interface. * * @param {WordArray|string} message The message to hash. * @param {WordArray|string} key The secret key. * * @return {WordArray} The HMAC. * * @static * * @example * * var hmac = CryptoJS.HmacMD5(message, key); */ C.HmacMD5 = Hasher._createHmacHelper(MD5); }(Math)); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var Hasher = C_lib.Hasher; var C_algo = C.algo; // Reusable object var W = []; /** * SHA-1 hash algorithm. */ var SHA1 = C_algo.SHA1 = Hasher.extend({ _doReset: function () { this._hash = new WordArray.init([ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ]); }, _doProcessBlock: function (M, offset) { // Shortcut var H = this._hash.words; // Working variables var a = H[0]; var b = H[1]; var c = H[2]; var d = H[3]; var e = H[4]; // Computation for (var i = 0; i < 80; i++) { if (i < 16) { W[i] = M[offset + i] | 0; } else { var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; W[i] = (n << 1) | (n >>> 31); } var t = ((a << 5) | (a >>> 27)) + e + W[i]; if (i < 20) { t += ((b & c) | (~b & d)) + 0x5a827999; } else if (i < 40) { t += (b ^ c ^ d) + 0x6ed9eba1; } else if (i < 60) { t += ((b & c) | (b & d) | (c & d)) - 0x70e44324; } else /* if (i < 80) */ { t += (b ^ c ^ d) - 0x359d3e2a; } e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = t; } // Intermediate hash value H[0] = (H[0] + a) | 0; H[1] = (H[1] + b) | 0; H[2] = (H[2] + c) | 0; H[3] = (H[3] + d) | 0; H[4] = (H[4] + e) | 0; }, _doFinalize: function () { // Shortcuts var data = this._data; var dataWords = data.words; var nBitsTotal = this._nDataBytes * 8; var nBitsLeft = data.sigBytes * 8; // Add padding dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; data.sigBytes = dataWords.length * 4; // Hash final blocks this._process(); // Return final computed hash return this._hash; }, clone: function () { var clone = Hasher.clone.call(this); clone._hash = this._hash.clone(); return clone; } }); /** * Shortcut function to the hasher's object interface. * * @param {WordArray|string} message The message to hash. * * @return {WordArray} The hash. * * @static * * @example * * var hash = CryptoJS.SHA1('message'); * var hash = CryptoJS.SHA1(wordArray); */ C.SHA1 = Hasher._createHelper(SHA1); /** * Shortcut function to the HMAC's object interface. * * @param {WordArray|string} message The message to hash. * @param {WordArray|string} key The secret key. * * @return {WordArray} The HMAC. * * @static * * @example * * var hmac = CryptoJS.HmacSHA1(message, key); */ C.HmacSHA1 = Hasher._createHmacHelper(SHA1); }()); (function (Math) { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var Hasher = C_lib.Hasher; var C_algo = C.algo; // Initialization and round constants tables var H = []; var K = []; // Compute constants (function () { function isPrime(n) { var sqrtN = Math.sqrt(n); for (var factor = 2; factor <= sqrtN; factor++) { if (!(n % factor)) { return false; } } return true; } function getFractionalBits(n) { return ((n - (n | 0)) * 0x100000000) | 0; } var n = 2; var nPrime = 0; while (nPrime < 64) { if (isPrime(n)) { if (nPrime < 8) { H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2)); } K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3)); nPrime++; } n++; } }()); // Reusable object var W = []; /** * SHA-256 hash algorithm. */ var SHA256 = C_algo.SHA256 = Hasher.extend({ _doReset: function () { this._hash = new WordArray.init(H.slice(0)); }, _doProcessBlock: function (M, offset) { // Shortcut var H = this._hash.words; // Working variables var a = H[0]; var b = H[1]; var c = H[2]; var d = H[3]; var e = H[4]; var f = H[5]; var g = H[6]; var h = H[7]; // Computation for (var i = 0; i < 64; i++) { if (i < 16) { W[i] = M[offset + i] | 0; } else { var gamma0x = W[i - 15]; var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ ((gamma0x << 14) | (gamma0x >>> 18)) ^ (gamma0x >>> 3); var gamma1x = W[i - 2]; var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ ((gamma1x << 13) | (gamma1x >>> 19)) ^ (gamma1x >>> 10); W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; } var ch = (e & f) ^ (~e & g); var maj = (a & b) ^ (a & c) ^ (b & c); var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22)); var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25)); var t1 = h + sigma1 + ch + K[i] + W[i]; var t2 = sigma0 + maj; h = g; g = f; f = e; e = (d + t1) | 0; d = c; c = b; b = a; a = (t1 + t2) | 0; } // Intermediate hash value H[0] = (H[0] + a) | 0; H[1] = (H[1] + b) | 0; H[2] = (H[2] + c) | 0; H[3] = (H[3] + d) | 0; H[4] = (H[4] + e) | 0; H[5] = (H[5] + f) | 0; H[6] = (H[6] + g) | 0; H[7] = (H[7] + h) | 0; }, _doFinalize: function () { // Shortcuts var data = this._data; var dataWords = data.words; var nBitsTotal = this._nDataBytes * 8; var nBitsLeft = data.sigBytes * 8; // Add padding dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; data.sigBytes = dataWords.length * 4; // Hash final blocks this._process(); // Return final computed hash return this._hash; }, clone: function () { var clone = Hasher.clone.call(this); clone._hash = this._hash.clone(); return clone; } }); /** * Shortcut function to the hasher's object interface. * * @param {WordArray|string} message The message to hash. * * @return {WordArray} The hash. * * @static * * @example * * var hash = CryptoJS.SHA256('message'); * var hash = CryptoJS.SHA256(wordArray); */ C.SHA256 = Hasher._createHelper(SHA256); /** * Shortcut function to the HMAC's object interface. * * @param {WordArray|string} message The message to hash. * @param {WordArray|string} key The secret key. * * @return {WordArray} The HMAC. * * @static * * @example * * var hmac = CryptoJS.HmacSHA256(message, key); */ C.HmacSHA256 = Hasher._createHmacHelper(SHA256); }(Math)); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var C_enc = C.enc; /** * UTF-16 BE encoding strategy. */ var Utf16BE = C_enc.Utf16 = C_enc.Utf16BE = { /** * Converts a word array to a UTF-16 BE string. * * @param {WordArray} wordArray The word array. * * @return {string} The UTF-16 BE string. * * @static * * @example * * var utf16String = CryptoJS.enc.Utf16.stringify(wordArray); */ stringify: function (wordArray) { // Shortcuts var words = wordArray.words; var sigBytes = wordArray.sigBytes; // Convert var utf16Chars = []; for (var i = 0; i < sigBytes; i += 2) { var codePoint = (words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff; utf16Chars.push(String.fromCharCode(codePoint)); } return utf16Chars.join(''); }, /** * Converts a UTF-16 BE string to a word array. * * @param {string} utf16Str The UTF-16 BE string. * * @return {WordArray} The word array. * * @static * * @example * * var wordArray = CryptoJS.enc.Utf16.parse(utf16String); */ parse: function (utf16Str) { // Shortcut var utf16StrLength = utf16Str.length; // Convert var words = []; for (var i = 0; i < utf16StrLength; i++) { words[i >>> 1] |= utf16Str.charCodeAt(i) << (16 - (i % 2) * 16); } return WordArray.create(words, utf16StrLength * 2); } }; /** * UTF-16 LE encoding strategy. */ C_enc.Utf16LE = { /** * Converts a word array to a UTF-16 LE string. * * @param {WordArray} wordArray The word array. * * @return {string} The UTF-16 LE string. * * @static * * @example * * var utf16Str = CryptoJS.enc.Utf16LE.stringify(wordArray); */ stringify: function (wordArray) { // Shortcuts var words = wordArray.words; var sigBytes = wordArray.sigBytes; // Convert var utf16Chars = []; for (var i = 0; i < sigBytes; i += 2) { var codePoint = swapEndian((words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff); utf16Chars.push(String.fromCharCode(codePoint)); } return utf16Chars.join(''); }, /** * Converts a UTF-16 LE string to a word array. * * @param {string} utf16Str The UTF-16 LE string. * * @return {WordArray} The word array. * * @static * * @example * * var wordArray = CryptoJS.enc.Utf16LE.parse(utf16Str); */ parse: function (utf16Str) { // Shortcut var utf16StrLength = utf16Str.length; // Convert var words = []; for (var i = 0; i < utf16StrLength; i++) { words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << (16 - (i % 2) * 16)); } return WordArray.create(words, utf16StrLength * 2); } }; function swapEndian(word) { return ((word << 8) & 0xff00ff00) | ((word >>> 8) & 0x00ff00ff); } }()); (function () { // Check if typed arrays are supported if (typeof ArrayBuffer != 'function') { return; } // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; // Reference original init var superInit = WordArray.init; // Augment WordArray.init to handle typed arrays var subInit = WordArray.init = function (typedArray) { // Convert buffers to uint8 if (typedArray instanceof ArrayBuffer) { typedArray = new Uint8Array(typedArray); } // Convert other array views to uint8 if ( typedArray instanceof Int8Array || (typeof Uint8ClampedArray !== "undefined" && typedArray instanceof Uint8ClampedArray) || typedArray instanceof Int16Array || typedArray instanceof Uint16Array || typedArray instanceof Int32Array || typedArray instanceof Uint32Array || typedArray instanceof Float32Array || typedArray instanceof Float64Array ) { typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); } // Handle Uint8Array if (typedArray instanceof Uint8Array) { // Shortcut var typedArrayByteLength = typedArray.byteLength; // Extract bytes var words = []; for (var i = 0; i < typedArrayByteLength; i++) { words[i >>> 2] |= typedArray[i] << (24 - (i % 4) * 8); } // Initialize this word array superInit.call(this, words, typedArrayByteLength); } else { // Else call normal init superInit.apply(this, arguments); } }; subInit.prototype = WordArray; }()); /** @preserve (c) 2012 by Cédric Mesnil. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ (function (Math) { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var Hasher = C_lib.Hasher; var C_algo = C.algo; // Constants table var _zl = WordArray.create([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]); var _zr = WordArray.create([ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]); var _sl = WordArray.create([ 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ]); var _sr = WordArray.create([ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ]); var _hl = WordArray.create([ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]); var _hr = WordArray.create([ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]); /** * RIPEMD160 hash algorithm. */ var RIPEMD160 = C_algo.RIPEMD160 = Hasher.extend({ _doReset: function () { this._hash = WordArray.create([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]); }, _doProcessBlock: function (M, offset) { // Swap endian for (var i = 0; i < 16; i++) { // Shortcuts var offset_i = offset + i; var M_offset_i = M[offset_i]; // Swap M[offset_i] = ( (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) ); } // Shortcut var H = this._hash.words; var hl = _hl.words; var hr = _hr.words; var zl = _zl.words; var zr = _zr.words; var sl = _sl.words; var sr = _sr.words; // Working variables var al, bl, cl, dl, el; var ar, br, cr, dr, er; ar = al = H[0]; br = bl = H[1]; cr = cl = H[2]; dr = dl = H[3]; er = el = H[4]; // Computation var t; for (var i = 0; i < 80; i += 1) { t = (al + M[offset+zl[i]])|0; if (i<16){ t += f1(bl,cl,dl) + hl[0]; } else if (i<32) { t += f2(bl,cl,dl) + hl[1]; } else if (i<48) { t += f3(bl,cl,dl) + hl[2]; } else if (i<64) { t += f4(bl,cl,dl) + hl[3]; } else {// if (i<80) { t += f5(bl,cl,dl) + hl[4]; } t = t|0; t = rotl(t,sl[i]); t = (t+el)|0; al = el; el = dl; dl = rotl(cl, 10); cl = bl; bl = t; t = (ar + M[offset+zr[i]])|0; if (i<16){ t += f5(br,cr,dr) + hr[0]; } else if (i<32) { t += f4(br,cr,dr) + hr[1]; } else if (i<48) { t += f3(br,cr,dr) + hr[2]; } else if (i<64) { t += f2(br,cr,dr) + hr[3]; } else {// if (i<80) { t += f1(br,cr,dr) + hr[4]; } t = t|0; t = rotl(t,sr[i]) ; t = (t+er)|0; ar = er; er = dr; dr = rotl(cr, 10); cr = br; br = t; } // Intermediate hash value t = (H[1] + cl + dr)|0; H[1] = (H[2] + dl + er)|0; H[2] = (H[3] + el + ar)|0; H[3] = (H[4] + al + br)|0; H[4] = (H[0] + bl + cr)|0; H[0] = t; }, _doFinalize: function () { // Shortcuts var data = this._data; var dataWords = data.words; var nBitsTotal = this._nDataBytes * 8; var nBitsLeft = data.sigBytes * 8; // Add padding dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( (((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) | (((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00) ); data.sigBytes = (dataWords.length + 1) * 4; // Hash final blocks this._process(); // Shortcuts var hash = this._hash; var H = hash.words; // Swap endian for (var i = 0; i < 5; i++) { // Shortcut var H_i = H[i]; // Swap H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); } // Return final computed hash return hash; }, clone: function () { var clone = Hasher.clone.call(this); clone._hash = this._hash.clone(); return clone; } }); function f1(x, y, z) { return ((x) ^ (y) ^ (z)); } function f2(x, y, z) { return (((x)&(y)) | ((~x)&(z))); } function f3(x, y, z) { return (((x) | (~(y))) ^ (z)); } function f4(x, y, z) { return (((x) & (z)) | ((y)&(~(z)))); } function f5(x, y, z) { return ((x) ^ ((y) |(~(z)))); } function rotl(x,n) { return (x<>>(32-n)); } /** * Shortcut function to the hasher's object interface. * * @param {WordArray|string} message The message to hash. * * @return {WordArray} The hash. * * @static * * @example * * var hash = CryptoJS.RIPEMD160('message'); * var hash = CryptoJS.RIPEMD160(wordArray); */ C.RIPEMD160 = Hasher._createHelper(RIPEMD160); /** * Shortcut function to the HMAC's object interface. * * @param {WordArray|string} message The message to hash. * @param {WordArray|string} key The secret key. * * @return {WordArray} The HMAC. * * @static * * @example * * var hmac = CryptoJS.HmacRIPEMD160(message, key); */ C.HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160); }(Math)); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var Base = C_lib.Base; var C_enc = C.enc; var Utf8 = C_enc.Utf8; var C_algo = C.algo; /** * HMAC algorithm. */ var HMAC = C_algo.HMAC = Base.extend({ /** * Initializes a newly created HMAC. * * @param {Hasher} hasher The hash algorithm to use. * @param {WordArray|string} key The secret key. * * @example * * var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key); */ init: function (hasher, key) { // Init hasher hasher = this._hasher = new hasher.init(); // Convert string to WordArray, else assume WordArray already if (typeof key == 'string') { key = Utf8.parse(key); } // Shortcuts var hasherBlockSize = hasher.blockSize; var hasherBlockSizeBytes = hasherBlockSize * 4; // Allow arbitrary length keys if (key.sigBytes > hasherBlockSizeBytes) { key = hasher.finalize(key); } // Clamp excess bits key.clamp(); // Clone key for inner and outer pads var oKey = this._oKey = key.clone(); var iKey = this._iKey = key.clone(); // Shortcuts var oKeyWords = oKey.words; var iKeyWords = iKey.words; // XOR keys with pad constants for (var i = 0; i < hasherBlockSize; i++) { oKeyWords[i] ^= 0x5c5c5c5c; iKeyWords[i] ^= 0x36363636; } oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes; // Set initial values this.reset(); }, /** * Resets this HMAC to its initial state. * * @example * * hmacHasher.reset(); */ reset: function () { // Shortcut var hasher = this._hasher; // Reset hasher.reset(); hasher.update(this._iKey); }, /** * Updates this HMAC with a message. * * @param {WordArray|string} messageUpdate The message to append. * * @return {HMAC} This HMAC instance. * * @example * * hmacHasher.update('message'); * hmacHasher.update(wordArray); */ update: function (messageUpdate) { this._hasher.update(messageUpdate); // Chainable return this; }, /** * Finalizes the HMAC computation. * Note that the finalize operation is effectively a destructive, read-once operation. * * @param {WordArray|string} messageUpdate (Optional) A final message update. * * @return {WordArray} The HMAC. * * @example * * var hmac = hmacHasher.finalize(); * var hmac = hmacHasher.finalize('message'); * var hmac = hmacHasher.finalize(wordArray); */ finalize: function (messageUpdate) { // Shortcut var hasher = this._hasher; // Compute HMAC var innerHash = hasher.finalize(messageUpdate); hasher.reset(); var hmac = hasher.finalize(this._oKey.clone().concat(innerHash)); return hmac; } }); }()); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var Base = C_lib.Base; var WordArray = C_lib.WordArray; var C_algo = C.algo; var SHA1 = C_algo.SHA1; var HMAC = C_algo.HMAC; /** * Password-Based Key Derivation Function 2 algorithm. */ var PBKDF2 = C_algo.PBKDF2 = Base.extend({ /** * Configuration options. * * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) * @property {Hasher} hasher The hasher to use. Default: SHA1 * @property {number} iterations The number of iterations to perform. Default: 1 */ cfg: Base.extend({ keySize: 128/32, hasher: SHA1, iterations: 1 }), /** * Initializes a newly created key derivation function. * * @param {Object} cfg (Optional) The configuration options to use for the derivation. * * @example * * var kdf = CryptoJS.algo.PBKDF2.create(); * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8 }); * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8, iterations: 1000 }); */ init: function (cfg) { this.cfg = this.cfg.extend(cfg); }, /** * Computes the Password-Based Key Derivation Function 2. * * @param {WordArray|string} password The password. * @param {WordArray|string} salt A salt. * * @return {WordArray} The derived key. * * @example * * var key = kdf.compute(password, salt); */ compute: function (password, salt) { // Shortcut var cfg = this.cfg; // Init HMAC var hmac = HMAC.create(cfg.hasher, password); // Initial values var derivedKey = WordArray.create(); var blockIndex = WordArray.create([0x00000001]); // Shortcuts var derivedKeyWords = derivedKey.words; var blockIndexWords = blockIndex.words; var keySize = cfg.keySize; var iterations = cfg.iterations; // Generate key while (derivedKeyWords.length < keySize) { var block = hmac.update(salt).finalize(blockIndex); hmac.reset(); // Shortcuts var blockWords = block.words; var blockWordsLength = blockWords.length; // Iterations var intermediate = block; for (var i = 1; i < iterations; i++) { intermediate = hmac.finalize(intermediate); hmac.reset(); // Shortcut var intermediateWords = intermediate.words; // XOR intermediate with block for (var j = 0; j < blockWordsLength; j++) { blockWords[j] ^= intermediateWords[j]; } } derivedKey.concat(block); blockIndexWords[0]++; } derivedKey.sigBytes = keySize * 4; return derivedKey; } }); /** * Computes the Password-Based Key Derivation Function 2. * * @param {WordArray|string} password The password. * @param {WordArray|string} salt A salt. * @param {Object} cfg (Optional) The configuration options to use for this computation. * * @return {WordArray} The derived key. * * @static * * @example * * var key = CryptoJS.PBKDF2(password, salt); * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 }); * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8, iterations: 1000 }); */ C.PBKDF2 = function (password, salt, cfg) { return PBKDF2.create(cfg).compute(password, salt); }; }()); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var Base = C_lib.Base; var WordArray = C_lib.WordArray; var C_algo = C.algo; var MD5 = C_algo.MD5; /** * This key derivation function is meant to conform with EVP_BytesToKey. * www.openssl.org/docs/crypto/EVP_BytesToKey.html */ var EvpKDF = C_algo.EvpKDF = Base.extend({ /** * Configuration options. * * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) * @property {Hasher} hasher The hash algorithm to use. Default: MD5 * @property {number} iterations The number of iterations to perform. Default: 1 */ cfg: Base.extend({ keySize: 128/32, hasher: MD5, iterations: 1 }), /** * Initializes a newly created key derivation function. * * @param {Object} cfg (Optional) The configuration options to use for the derivation. * * @example * * var kdf = CryptoJS.algo.EvpKDF.create(); * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 }); * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 }); */ init: function (cfg) { this.cfg = this.cfg.extend(cfg); }, /** * Derives a key from a password. * * @param {WordArray|string} password The password. * @param {WordArray|string} salt A salt. * * @return {WordArray} The derived key. * * @example * * var key = kdf.compute(password, salt); */ compute: function (password, salt) { // Shortcut var cfg = this.cfg; // Init hasher var hasher = cfg.hasher.create(); // Initial values var derivedKey = WordArray.create(); // Shortcuts var derivedKeyWords = derivedKey.words; var keySize = cfg.keySize; var iterations = cfg.iterations; // Generate key while (derivedKeyWords.length < keySize) { if (block) { hasher.update(block); } var block = hasher.update(password).finalize(salt); hasher.reset(); // Iterations for (var i = 1; i < iterations; i++) { block = hasher.finalize(block); hasher.reset(); } derivedKey.concat(block); } derivedKey.sigBytes = keySize * 4; return derivedKey; } }); /** * Derives a key from a password. * * @param {WordArray|string} password The password. * @param {WordArray|string} salt A salt. * @param {Object} cfg (Optional) The configuration options to use for this computation. * * @return {WordArray} The derived key. * * @static * * @example * * var key = CryptoJS.EvpKDF(password, salt); * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 }); * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 }); */ C.EvpKDF = function (password, salt, cfg) { return EvpKDF.create(cfg).compute(password, salt); }; }()); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var C_algo = C.algo; var SHA256 = C_algo.SHA256; /** * SHA-224 hash algorithm. */ var SHA224 = C_algo.SHA224 = SHA256.extend({ _doReset: function () { this._hash = new WordArray.init([ 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 ]); }, _doFinalize: function () { var hash = SHA256._doFinalize.call(this); hash.sigBytes -= 4; return hash; } }); /** * Shortcut function to the hasher's object interface. * * @param {WordArray|string} message The message to hash. * * @return {WordArray} The hash. * * @static * * @example * * var hash = CryptoJS.SHA224('message'); * var hash = CryptoJS.SHA224(wordArray); */ C.SHA224 = SHA256._createHelper(SHA224); /** * Shortcut function to the HMAC's object interface. * * @param {WordArray|string} message The message to hash. * @param {WordArray|string} key The secret key. * * @return {WordArray} The HMAC. * * @static * * @example * * var hmac = CryptoJS.HmacSHA224(message, key); */ C.HmacSHA224 = SHA256._createHmacHelper(SHA224); }()); (function (undefined) { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var Base = C_lib.Base; var X32WordArray = C_lib.WordArray; /** * x64 namespace. */ var C_x64 = C.x64 = {}; /** * A 64-bit word. */ var X64Word = C_x64.Word = Base.extend({ /** * Initializes a newly created 64-bit word. * * @param {number} high The high 32 bits. * @param {number} low The low 32 bits. * * @example * * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607); */ init: function (high, low) { this.high = high; this.low = low; } /** * Bitwise NOTs this word. * * @return {X64Word} A new x64-Word object after negating. * * @example * * var negated = x64Word.not(); */ // not: function () { // var high = ~this.high; // var low = ~this.low; // return X64Word.create(high, low); // }, /** * Bitwise ANDs this word with the passed word. * * @param {X64Word} word The x64-Word to AND with this word. * * @return {X64Word} A new x64-Word object after ANDing. * * @example * * var anded = x64Word.and(anotherX64Word); */ // and: function (word) { // var high = this.high & word.high; // var low = this.low & word.low; // return X64Word.create(high, low); // }, /** * Bitwise ORs this word with the passed word. * * @param {X64Word} word The x64-Word to OR with this word. * * @return {X64Word} A new x64-Word object after ORing. * * @example * * var ored = x64Word.or(anotherX64Word); */ // or: function (word) { // var high = this.high | word.high; // var low = this.low | word.low; // return X64Word.create(high, low); // }, /** * Bitwise XORs this word with the passed word. * * @param {X64Word} word The x64-Word to XOR with this word. * * @return {X64Word} A new x64-Word object after XORing. * * @example * * var xored = x64Word.xor(anotherX64Word); */ // xor: function (word) { // var high = this.high ^ word.high; // var low = this.low ^ word.low; // return X64Word.create(high, low); // }, /** * Shifts this word n bits to the left. * * @param {number} n The number of bits to shift. * * @return {X64Word} A new x64-Word object after shifting. * * @example * * var shifted = x64Word.shiftL(25); */ // shiftL: function (n) { // if (n < 32) { // var high = (this.high << n) | (this.low >>> (32 - n)); // var low = this.low << n; // } else { // var high = this.low << (n - 32); // var low = 0; // } // return X64Word.create(high, low); // }, /** * Shifts this word n bits to the right. * * @param {number} n The number of bits to shift. * * @return {X64Word} A new x64-Word object after shifting. * * @example * * var shifted = x64Word.shiftR(7); */ // shiftR: function (n) { // if (n < 32) { // var low = (this.low >>> n) | (this.high << (32 - n)); // var high = this.high >>> n; // } else { // var low = this.high >>> (n - 32); // var high = 0; // } // return X64Word.create(high, low); // }, /** * Rotates this word n bits to the left. * * @param {number} n The number of bits to rotate. * * @return {X64Word} A new x64-Word object after rotating. * * @example * * var rotated = x64Word.rotL(25); */ // rotL: function (n) { // return this.shiftL(n).or(this.shiftR(64 - n)); // }, /** * Rotates this word n bits to the right. * * @param {number} n The number of bits to rotate. * * @return {X64Word} A new x64-Word object after rotating. * * @example * * var rotated = x64Word.rotR(7); */ // rotR: function (n) { // return this.shiftR(n).or(this.shiftL(64 - n)); // }, /** * Adds this word with the passed word. * * @param {X64Word} word The x64-Word to add with this word. * * @return {X64Word} A new x64-Word object after adding. * * @example * * var added = x64Word.add(anotherX64Word); */ // add: function (word) { // var low = (this.low + word.low) | 0; // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0; // var high = (this.high + word.high + carry) | 0; // return X64Word.create(high, low); // } }); /** * An array of 64-bit words. * * @property {Array} words The array of CryptoJS.x64.Word objects. * @property {number} sigBytes The number of significant bytes in this word array. */ var X64WordArray = C_x64.WordArray = Base.extend({ /** * Initializes a newly created word array. * * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects. * @param {number} sigBytes (Optional) The number of significant bytes in the words. * * @example * * var wordArray = CryptoJS.x64.WordArray.create(); * * var wordArray = CryptoJS.x64.WordArray.create([ * CryptoJS.x64.Word.create(0x00010203, 0x04050607), * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) * ]); * * var wordArray = CryptoJS.x64.WordArray.create([ * CryptoJS.x64.Word.create(0x00010203, 0x04050607), * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) * ], 10); */ init: function (words, sigBytes) { words = this.words = words || []; if (sigBytes != undefined) { this.sigBytes = sigBytes; } else { this.sigBytes = words.length * 8; } }, /** * Converts this 64-bit word array to a 32-bit word array. * * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array. * * @example * * var x32WordArray = x64WordArray.toX32(); */ toX32: function () { // Shortcuts var x64Words = this.words; var x64WordsLength = x64Words.length; // Convert var x32Words = []; for (var i = 0; i < x64WordsLength; i++) { var x64Word = x64Words[i]; x32Words.push(x64Word.high); x32Words.push(x64Word.low); } return X32WordArray.create(x32Words, this.sigBytes); }, /** * Creates a copy of this word array. * * @return {X64WordArray} The clone. * * @example * * var clone = x64WordArray.clone(); */ clone: function () { var clone = Base.clone.call(this); // Clone "words" array var words = clone.words = this.words.slice(0); // Clone each X64Word object var wordsLength = words.length; for (var i = 0; i < wordsLength; i++) { words[i] = words[i].clone(); } return clone; } }); }()); (function (Math) { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var Hasher = C_lib.Hasher; var C_x64 = C.x64; var X64Word = C_x64.Word; var C_algo = C.algo; // Constants tables var RHO_OFFSETS = []; var PI_INDEXES = []; var ROUND_CONSTANTS = []; // Compute Constants (function () { // Compute rho offset constants var x = 1, y = 0; for (var t = 0; t < 24; t++) { RHO_OFFSETS[x + 5 * y] = ((t + 1) * (t + 2) / 2) % 64; var newX = y % 5; var newY = (2 * x + 3 * y) % 5; x = newX; y = newY; } // Compute pi index constants for (var x = 0; x < 5; x++) { for (var y = 0; y < 5; y++) { PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5; } } // Compute round constants var LFSR = 0x01; for (var i = 0; i < 24; i++) { var roundConstantMsw = 0; var roundConstantLsw = 0; for (var j = 0; j < 7; j++) { if (LFSR & 0x01) { var bitPosition = (1 << j) - 1; if (bitPosition < 32) { roundConstantLsw ^= 1 << bitPosition; } else /* if (bitPosition >= 32) */ { roundConstantMsw ^= 1 << (bitPosition - 32); } } // Compute next LFSR if (LFSR & 0x80) { // Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1 LFSR = (LFSR << 1) ^ 0x71; } else { LFSR <<= 1; } } ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw); } }()); // Reusable objects for temporary values var T = []; (function () { for (var i = 0; i < 25; i++) { T[i] = X64Word.create(); } }()); /** * SHA-3 hash algorithm. */ var SHA3 = C_algo.SHA3 = Hasher.extend({ /** * Configuration options. * * @property {number} outputLength * The desired number of bits in the output hash. * Only values permitted are: 224, 256, 384, 512. * Default: 512 */ cfg: Hasher.cfg.extend({ outputLength: 512 }), _doReset: function () { var state = this._state = [] for (var i = 0; i < 25; i++) { state[i] = new X64Word.init(); } this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32; }, _doProcessBlock: function (M, offset) { // Shortcuts var state = this._state; var nBlockSizeLanes = this.blockSize / 2; // Absorb for (var i = 0; i < nBlockSizeLanes; i++) { // Shortcuts var M2i = M[offset + 2 * i]; var M2i1 = M[offset + 2 * i + 1]; // Swap endian M2i = ( (((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff) | (((M2i << 24) | (M2i >>> 8)) & 0xff00ff00) ); M2i1 = ( (((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff) | (((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00) ); // Absorb message into state var lane = state[i]; lane.high ^= M2i1; lane.low ^= M2i; } // Rounds for (var round = 0; round < 24; round++) { // Theta for (var x = 0; x < 5; x++) { // Mix column lanes var tMsw = 0, tLsw = 0; for (var y = 0; y < 5; y++) { var lane = state[x + 5 * y]; tMsw ^= lane.high; tLsw ^= lane.low; } // Temporary values var Tx = T[x]; Tx.high = tMsw; Tx.low = tLsw; } for (var x = 0; x < 5; x++) { // Shortcuts var Tx4 = T[(x + 4) % 5]; var Tx1 = T[(x + 1) % 5]; var Tx1Msw = Tx1.high; var Tx1Lsw = Tx1.low; // Mix surrounding columns var tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31)); var tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31)); for (var y = 0; y < 5; y++) { var lane = state[x + 5 * y]; lane.high ^= tMsw; lane.low ^= tLsw; } } // Rho Pi for (var laneIndex = 1; laneIndex < 25; laneIndex++) { // Shortcuts var lane = state[laneIndex]; var laneMsw = lane.high; var laneLsw = lane.low; var rhoOffset = RHO_OFFSETS[laneIndex]; // Rotate lanes if (rhoOffset < 32) { var tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset)); var tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset)); } else /* if (rhoOffset >= 32) */ { var tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset)); var tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset)); } // Transpose lanes var TPiLane = T[PI_INDEXES[laneIndex]]; TPiLane.high = tMsw; TPiLane.low = tLsw; } // Rho pi at x = y = 0 var T0 = T[0]; var state0 = state[0]; T0.high = state0.high; T0.low = state0.low; // Chi for (var x = 0; x < 5; x++) { for (var y = 0; y < 5; y++) { // Shortcuts var laneIndex = x + 5 * y; var lane = state[laneIndex]; var TLane = T[laneIndex]; var Tx1Lane = T[((x + 1) % 5) + 5 * y]; var Tx2Lane = T[((x + 2) % 5) + 5 * y]; // Mix rows lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high); lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low); } } // Iota var lane = state[0]; var roundConstant = ROUND_CONSTANTS[round]; lane.high ^= roundConstant.high; lane.low ^= roundConstant.low;; } }, _doFinalize: function () { // Shortcuts var data = this._data; var dataWords = data.words; var nBitsTotal = this._nDataBytes * 8; var nBitsLeft = data.sigBytes * 8; var blockSizeBits = this.blockSize * 32; // Add padding dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - nBitsLeft % 32); dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80; data.sigBytes = dataWords.length * 4; // Hash final blocks this._process(); // Shortcuts var state = this._state; var outputLengthBytes = this.cfg.outputLength / 8; var outputLengthLanes = outputLengthBytes / 8; // Squeeze var hashWords = []; for (var i = 0; i < outputLengthLanes; i++) { // Shortcuts var lane = state[i]; var laneMsw = lane.high; var laneLsw = lane.low; // Swap endian laneMsw = ( (((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff) | (((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00) ); laneLsw = ( (((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff) | (((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00) ); // Squeeze state to retrieve hash hashWords.push(laneLsw); hashWords.push(laneMsw); } // Return final computed hash return new WordArray.init(hashWords, outputLengthBytes); }, clone: function () { var clone = Hasher.clone.call(this); var state = clone._state = this._state.slice(0); for (var i = 0; i < 25; i++) { state[i] = state[i].clone(); } return clone; } }); /** * Shortcut function to the hasher's object interface. * * @param {WordArray|string} message The message to hash. * * @return {WordArray} The hash. * * @static * * @example * * var hash = CryptoJS.SHA3('message'); * var hash = CryptoJS.SHA3(wordArray); */ C.SHA3 = Hasher._createHelper(SHA3); /** * Shortcut function to the HMAC's object interface. * * @param {WordArray|string} message The message to hash. * @param {WordArray|string} key The secret key. * * @return {WordArray} The HMAC. * * @static * * @example * * var hmac = CryptoJS.HmacSHA3(message, key); */ C.HmacSHA3 = Hasher._createHmacHelper(SHA3); }(Math)); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var Hasher = C_lib.Hasher; var C_x64 = C.x64; var X64Word = C_x64.Word; var X64WordArray = C_x64.WordArray; var C_algo = C.algo; function X64Word_create() { return X64Word.create.apply(X64Word, arguments); } // Constants var K = [ X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd), X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc), X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019), X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118), X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe), X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2), X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1), X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694), X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3), X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65), X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483), X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5), X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210), X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4), X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725), X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70), X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926), X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df), X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8), X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b), X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001), X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30), X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910), X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8), X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53), X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8), X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb), X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3), X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60), X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec), X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9), X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b), X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207), X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178), X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6), X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b), X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493), X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c), X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a), X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817) ]; // Reusable objects var W = []; (function () { for (var i = 0; i < 80; i++) { W[i] = X64Word_create(); } }()); /** * SHA-512 hash algorithm. */ var SHA512 = C_algo.SHA512 = Hasher.extend({ _doReset: function () { this._hash = new X64WordArray.init([ new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b), new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1), new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f), new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179) ]); }, _doProcessBlock: function (M, offset) { // Shortcuts var H = this._hash.words; var H0 = H[0]; var H1 = H[1]; var H2 = H[2]; var H3 = H[3]; var H4 = H[4]; var H5 = H[5]; var H6 = H[6]; var H7 = H[7]; var H0h = H0.high; var H0l = H0.low; var H1h = H1.high; var H1l = H1.low; var H2h = H2.high; var H2l = H2.low; var H3h = H3.high; var H3l = H3.low; var H4h = H4.high; var H4l = H4.low; var H5h = H5.high; var H5l = H5.low; var H6h = H6.high; var H6l = H6.low; var H7h = H7.high; var H7l = H7.low; // Working variables var ah = H0h; var al = H0l; var bh = H1h; var bl = H1l; var ch = H2h; var cl = H2l; var dh = H3h; var dl = H3l; var eh = H4h; var el = H4l; var fh = H5h; var fl = H5l; var gh = H6h; var gl = H6l; var hh = H7h; var hl = H7l; // Rounds for (var i = 0; i < 80; i++) { // Shortcut var Wi = W[i]; // Extend message if (i < 16) { var Wih = Wi.high = M[offset + i * 2] | 0; var Wil = Wi.low = M[offset + i * 2 + 1] | 0; } else { // Gamma0 var gamma0x = W[i - 15]; var gamma0xh = gamma0x.high; var gamma0xl = gamma0x.low; var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7); var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25)); // Gamma1 var gamma1x = W[i - 2]; var gamma1xh = gamma1x.high; var gamma1xl = gamma1x.low; var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6); var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26)); // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16] var Wi7 = W[i - 7]; var Wi7h = Wi7.high; var Wi7l = Wi7.low; var Wi16 = W[i - 16]; var Wi16h = Wi16.high; var Wi16l = Wi16.low; var Wil = gamma0l + Wi7l; var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0); var Wil = Wil + gamma1l; var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0); var Wil = Wil + Wi16l; var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0); Wi.high = Wih; Wi.low = Wil; } var chh = (eh & fh) ^ (~eh & gh); var chl = (el & fl) ^ (~el & gl); var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch); var majl = (al & bl) ^ (al & cl) ^ (bl & cl); var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7)); var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7)); var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9)); var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9)); // t1 = h + sigma1 + ch + K[i] + W[i] var Ki = K[i]; var Kih = Ki.high; var Kil = Ki.low; var t1l = hl + sigma1l; var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0); var t1l = t1l + chl; var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0); var t1l = t1l + Kil; var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0); var t1l = t1l + Wil; var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0); // t2 = sigma0 + maj var t2l = sigma0l + majl; var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0); // Update working variables hh = gh; hl = gl; gh = fh; gl = fl; fh = eh; fl = el; el = (dl + t1l) | 0; eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0; dh = ch; dl = cl; ch = bh; cl = bl; bh = ah; bl = al; al = (t1l + t2l) | 0; ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0; } // Intermediate hash value H0l = H0.low = (H0l + al); H0.high = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0)); H1l = H1.low = (H1l + bl); H1.high = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0)); H2l = H2.low = (H2l + cl); H2.high = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0)); H3l = H3.low = (H3l + dl); H3.high = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0)); H4l = H4.low = (H4l + el); H4.high = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0)); H5l = H5.low = (H5l + fl); H5.high = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0)); H6l = H6.low = (H6l + gl); H6.high = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0)); H7l = H7.low = (H7l + hl); H7.high = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0)); }, _doFinalize: function () { // Shortcuts var data = this._data; var dataWords = data.words; var nBitsTotal = this._nDataBytes * 8; var nBitsLeft = data.sigBytes * 8; // Add padding dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 30] = Math.floor(nBitsTotal / 0x100000000); dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 31] = nBitsTotal; data.sigBytes = dataWords.length * 4; // Hash final blocks this._process(); // Convert hash to 32-bit word array before returning var hash = this._hash.toX32(); // Return final computed hash return hash; }, clone: function () { var clone = Hasher.clone.call(this); clone._hash = this._hash.clone(); return clone; }, blockSize: 1024/32 }); /** * Shortcut function to the hasher's object interface. * * @param {WordArray|string} message The message to hash. * * @return {WordArray} The hash. * * @static * * @example * * var hash = CryptoJS.SHA512('message'); * var hash = CryptoJS.SHA512(wordArray); */ C.SHA512 = Hasher._createHelper(SHA512); /** * Shortcut function to the HMAC's object interface. * * @param {WordArray|string} message The message to hash. * @param {WordArray|string} key The secret key. * * @return {WordArray} The HMAC. * * @static * * @example * * var hmac = CryptoJS.HmacSHA512(message, key); */ C.HmacSHA512 = Hasher._createHmacHelper(SHA512); }()); (function () { // Shortcuts var C = CryptoJS; var C_x64 = C.x64; var X64Word = C_x64.Word; var X64WordArray = C_x64.WordArray; var C_algo = C.algo; var SHA512 = C_algo.SHA512; /** * SHA-384 hash algorithm. */ var SHA384 = C_algo.SHA384 = SHA512.extend({ _doReset: function () { this._hash = new X64WordArray.init([ new X64Word.init(0xcbbb9d5d, 0xc1059ed8), new X64Word.init(0x629a292a, 0x367cd507), new X64Word.init(0x9159015a, 0x3070dd17), new X64Word.init(0x152fecd8, 0xf70e5939), new X64Word.init(0x67332667, 0xffc00b31), new X64Word.init(0x8eb44a87, 0x68581511), new X64Word.init(0xdb0c2e0d, 0x64f98fa7), new X64Word.init(0x47b5481d, 0xbefa4fa4) ]); }, _doFinalize: function () { var hash = SHA512._doFinalize.call(this); hash.sigBytes -= 16; return hash; } }); /** * Shortcut function to the hasher's object interface. * * @param {WordArray|string} message The message to hash. * * @return {WordArray} The hash. * * @static * * @example * * var hash = CryptoJS.SHA384('message'); * var hash = CryptoJS.SHA384(wordArray); */ C.SHA384 = SHA512._createHelper(SHA384); /** * Shortcut function to the HMAC's object interface. * * @param {WordArray|string} message The message to hash. * @param {WordArray|string} key The secret key. * * @return {WordArray} The HMAC. * * @static * * @example * * var hmac = CryptoJS.HmacSHA384(message, key); */ C.HmacSHA384 = SHA512._createHmacHelper(SHA384); }()); /** * Cipher core components. */ CryptoJS.lib.Cipher || (function (undefined) { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var Base = C_lib.Base; var WordArray = C_lib.WordArray; var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm; var C_enc = C.enc; var Utf8 = C_enc.Utf8; var Base64 = C_enc.Base64; var C_algo = C.algo; var EvpKDF = C_algo.EvpKDF; /** * Abstract base cipher template. * * @property {number} keySize This cipher's key size. Default: 4 (128 bits) * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits) * @property {number} _ENC_XFORM_MODE A constant representing encryption mode. * @property {number} _DEC_XFORM_MODE A constant representing decryption mode. */ var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({ /** * Configuration options. * * @property {WordArray} iv The IV to use for this operation. */ cfg: Base.extend(), /** * Creates this cipher in encryption mode. * * @param {WordArray} key The key. * @param {Object} cfg (Optional) The configuration options to use for this operation. * * @return {Cipher} A cipher instance. * * @static * * @example * * var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray }); */ createEncryptor: function (key, cfg) { return this.create(this._ENC_XFORM_MODE, key, cfg); }, /** * Creates this cipher in decryption mode. * * @param {WordArray} key The key. * @param {Object} cfg (Optional) The configuration options to use for this operation. * * @return {Cipher} A cipher instance. * * @static * * @example * * var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray }); */ createDecryptor: function (key, cfg) { return this.create(this._DEC_XFORM_MODE, key, cfg); }, /** * Initializes a newly created cipher. * * @param {number} xformMode Either the encryption or decryption transormation mode constant. * @param {WordArray} key The key. * @param {Object} cfg (Optional) The configuration options to use for this operation. * * @example * * var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray }); */ init: function (xformMode, key, cfg) { // Apply config defaults this.cfg = this.cfg.extend(cfg); // Store transform mode and key this._xformMode = xformMode; this._key = key; // Set initial values this.reset(); }, /** * Resets this cipher to its initial state. * * @example * * cipher.reset(); */ reset: function () { // Reset data buffer BufferedBlockAlgorithm.reset.call(this); // Perform concrete-cipher logic this._doReset(); }, /** * Adds data to be encrypted or decrypted. * * @param {WordArray|string} dataUpdate The data to encrypt or decrypt. * * @return {WordArray} The data after processing. * * @example * * var encrypted = cipher.process('data'); * var encrypted = cipher.process(wordArray); */ process: function (dataUpdate) { // Append this._append(dataUpdate); // Process available blocks return this._process(); }, /** * Finalizes the encryption or decryption process. * Note that the finalize operation is effectively a destructive, read-once operation. * * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt. * * @return {WordArray} The data after final processing. * * @example * * var encrypted = cipher.finalize(); * var encrypted = cipher.finalize('data'); * var encrypted = cipher.finalize(wordArray); */ finalize: function (dataUpdate) { // Final data update if (dataUpdate) { this._append(dataUpdate); } // Perform concrete-cipher logic var finalProcessedData = this._doFinalize(); return finalProcessedData; }, keySize: 128/32, ivSize: 128/32, _ENC_XFORM_MODE: 1, _DEC_XFORM_MODE: 2, /** * Creates shortcut functions to a cipher's object interface. * * @param {Cipher} cipher The cipher to create a helper for. * * @return {Object} An object with encrypt and decrypt shortcut functions. * * @static * * @example * * var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES); */ _createHelper: (function () { function selectCipherStrategy(key) { if (typeof key == 'string') { return PasswordBasedCipher; } else { return SerializableCipher; } } return function (cipher) { return { encrypt: function (message, key, cfg) { return selectCipherStrategy(key).encrypt(cipher, message, key, cfg); }, decrypt: function (ciphertext, key, cfg) { return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg); } }; }; }()) }); /** * Abstract base stream cipher template. * * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits) */ var StreamCipher = C_lib.StreamCipher = Cipher.extend({ _doFinalize: function () { // Process partial blocks var finalProcessedBlocks = this._process(!!'flush'); return finalProcessedBlocks; }, blockSize: 1 }); /** * Mode namespace. */ var C_mode = C.mode = {}; /** * Abstract base block cipher mode template. */ var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({ /** * Creates this mode for encryption. * * @param {Cipher} cipher A block cipher instance. * @param {Array} iv The IV words. * * @static * * @example * * var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words); */ createEncryptor: function (cipher, iv) { return this.Encryptor.create(cipher, iv); }, /** * Creates this mode for decryption. * * @param {Cipher} cipher A block cipher instance. * @param {Array} iv The IV words. * * @static * * @example * * var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words); */ createDecryptor: function (cipher, iv) { return this.Decryptor.create(cipher, iv); }, /** * Initializes a newly created mode. * * @param {Cipher} cipher A block cipher instance. * @param {Array} iv The IV words. * * @example * * var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words); */ init: function (cipher, iv) { this._cipher = cipher; this._iv = iv; } }); /** * Cipher Block Chaining mode. */ var CBC = C_mode.CBC = (function () { /** * Abstract base CBC mode. */ var CBC = BlockCipherMode.extend(); /** * CBC encryptor. */ CBC.Encryptor = CBC.extend({ /** * Processes the data block at offset. * * @param {Array} words The data words to operate on. * @param {number} offset The offset where the block starts. * * @example * * mode.processBlock(data.words, offset); */ processBlock: function (words, offset) { // Shortcuts var cipher = this._cipher; var blockSize = cipher.blockSize; // XOR and encrypt xorBlock.call(this, words, offset, blockSize); cipher.encryptBlock(words, offset); // Remember this block to use with next block this._prevBlock = words.slice(offset, offset + blockSize); } }); /** * CBC decryptor. */ CBC.Decryptor = CBC.extend({ /** * Processes the data block at offset. * * @param {Array} words The data words to operate on. * @param {number} offset The offset where the block starts. * * @example * * mode.processBlock(data.words, offset); */ processBlock: function (words, offset) { // Shortcuts var cipher = this._cipher; var blockSize = cipher.blockSize; // Remember this block to use with next block var thisBlock = words.slice(offset, offset + blockSize); // Decrypt and XOR cipher.decryptBlock(words, offset); xorBlock.call(this, words, offset, blockSize); // This block becomes the previous block this._prevBlock = thisBlock; } }); function xorBlock(words, offset, blockSize) { // Shortcut var iv = this._iv; // Choose mixing block if (iv) { var block = iv; // Remove IV for subsequent blocks this._iv = undefined; } else { var block = this._prevBlock; } // XOR blocks for (var i = 0; i < blockSize; i++) { words[offset + i] ^= block[i]; } } return CBC; }()); /** * Padding namespace. */ var C_pad = C.pad = {}; /** * PKCS #5/7 padding strategy. */ var Pkcs7 = C_pad.Pkcs7 = { /** * Pads data using the algorithm defined in PKCS #5/7. * * @param {WordArray} data The data to pad. * @param {number} blockSize The multiple that the data should be padded to. * * @static * * @example * * CryptoJS.pad.Pkcs7.pad(wordArray, 4); */ pad: function (data, blockSize) { // Shortcut var blockSizeBytes = blockSize * 4; // Count padding bytes var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; // Create padding word var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes; // Create padding var paddingWords = []; for (var i = 0; i < nPaddingBytes; i += 4) { paddingWords.push(paddingWord); } var padding = WordArray.create(paddingWords, nPaddingBytes); // Add padding data.concat(padding); }, /** * Unpads data that had been padded using the algorithm defined in PKCS #5/7. * * @param {WordArray} data The data to unpad. * * @static * * @example * * CryptoJS.pad.Pkcs7.unpad(wordArray); */ unpad: function (data) { // Get number of padding bytes from last byte var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; // Remove padding data.sigBytes -= nPaddingBytes; } }; /** * Abstract base block cipher template. * * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits) */ var BlockCipher = C_lib.BlockCipher = Cipher.extend({ /** * Configuration options. * * @property {Mode} mode The block mode to use. Default: CBC * @property {Padding} padding The padding strategy to use. Default: Pkcs7 */ cfg: Cipher.cfg.extend({ mode: CBC, padding: Pkcs7 }), reset: function () { // Reset cipher Cipher.reset.call(this); // Shortcuts var cfg = this.cfg; var iv = cfg.iv; var mode = cfg.mode; // Reset block mode if (this._xformMode == this._ENC_XFORM_MODE) { var modeCreator = mode.createEncryptor; } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { var modeCreator = mode.createDecryptor; // Keep at least one block in the buffer for unpadding this._minBufferSize = 1; } if (this._mode && this._mode.__creator == modeCreator) { this._mode.init(this, iv && iv.words); } else { this._mode = modeCreator.call(mode, this, iv && iv.words); this._mode.__creator = modeCreator; } }, _doProcessBlock: function (words, offset) { this._mode.processBlock(words, offset); }, _doFinalize: function () { // Shortcut var padding = this.cfg.padding; // Finalize if (this._xformMode == this._ENC_XFORM_MODE) { // Pad data padding.pad(this._data, this.blockSize); // Process final blocks var finalProcessedBlocks = this._process(!!'flush'); } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { // Process final blocks var finalProcessedBlocks = this._process(!!'flush'); // Unpad data padding.unpad(finalProcessedBlocks); } return finalProcessedBlocks; }, blockSize: 128/32 }); /** * A collection of cipher parameters. * * @property {WordArray} ciphertext The raw ciphertext. * @property {WordArray} key The key to this ciphertext. * @property {WordArray} iv The IV used in the ciphering operation. * @property {WordArray} salt The salt used with a key derivation function. * @property {Cipher} algorithm The cipher algorithm. * @property {Mode} mode The block mode used in the ciphering operation. * @property {Padding} padding The padding scheme used in the ciphering operation. * @property {number} blockSize The block size of the cipher. * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string. */ var CipherParams = C_lib.CipherParams = Base.extend({ /** * Initializes a newly created cipher params object. * * @param {Object} cipherParams An object with any of the possible cipher parameters. * * @example * * var cipherParams = CryptoJS.lib.CipherParams.create({ * ciphertext: ciphertextWordArray, * key: keyWordArray, * iv: ivWordArray, * salt: saltWordArray, * algorithm: CryptoJS.algo.AES, * mode: CryptoJS.mode.CBC, * padding: CryptoJS.pad.PKCS7, * blockSize: 4, * formatter: CryptoJS.format.OpenSSL * }); */ init: function (cipherParams) { this.mixIn(cipherParams); }, /** * Converts this cipher params object to a string. * * @param {Format} formatter (Optional) The formatting strategy to use. * * @return {string} The stringified cipher params. * * @throws Error If neither the formatter nor the default formatter is set. * * @example * * var string = cipherParams + ''; * var string = cipherParams.toString(); * var string = cipherParams.toString(CryptoJS.format.OpenSSL); */ toString: function (formatter) { return (formatter || this.formatter).stringify(this); } }); /** * Format namespace. */ var C_format = C.format = {}; /** * OpenSSL formatting strategy. */ var OpenSSLFormatter = C_format.OpenSSL = { /** * Converts a cipher params object to an OpenSSL-compatible string. * * @param {CipherParams} cipherParams The cipher params object. * * @return {string} The OpenSSL-compatible string. * * @static * * @example * * var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams); */ stringify: function (cipherParams) { // Shortcuts var ciphertext = cipherParams.ciphertext; var salt = cipherParams.salt; // Format if (salt) { var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext); } else { var wordArray = ciphertext; } return wordArray.toString(Base64); }, /** * Converts an OpenSSL-compatible string to a cipher params object. * * @param {string} openSSLStr The OpenSSL-compatible string. * * @return {CipherParams} The cipher params object. * * @static * * @example * * var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString); */ parse: function (openSSLStr) { // Parse base64 var ciphertext = Base64.parse(openSSLStr); // Shortcut var ciphertextWords = ciphertext.words; // Test for salt if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) { // Extract salt var salt = WordArray.create(ciphertextWords.slice(2, 4)); // Remove salt from ciphertext ciphertextWords.splice(0, 4); ciphertext.sigBytes -= 16; } return CipherParams.create({ ciphertext: ciphertext, salt: salt }); } }; /** * A cipher wrapper that returns ciphertext as a serializable cipher params object. */ var SerializableCipher = C_lib.SerializableCipher = Base.extend({ /** * Configuration options. * * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL */ cfg: Base.extend({ format: OpenSSLFormatter }), /** * Encrypts a message. * * @param {Cipher} cipher The cipher algorithm to use. * @param {WordArray|string} message The message to encrypt. * @param {WordArray} key The key. * @param {Object} cfg (Optional) The configuration options to use for this operation. * * @return {CipherParams} A cipher params object. * * @static * * @example * * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key); * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv }); * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL }); */ encrypt: function (cipher, message, key, cfg) { // Apply config defaults cfg = this.cfg.extend(cfg); // Encrypt var encryptor = cipher.createEncryptor(key, cfg); var ciphertext = encryptor.finalize(message); // Shortcut var cipherCfg = encryptor.cfg; // Create and return serializable cipher params return CipherParams.create({ ciphertext: ciphertext, key: key, iv: cipherCfg.iv, algorithm: cipher, mode: cipherCfg.mode, padding: cipherCfg.padding, blockSize: cipher.blockSize, formatter: cfg.format }); }, /** * Decrypts serialized ciphertext. * * @param {Cipher} cipher The cipher algorithm to use. * @param {CipherParams|string} ciphertext The ciphertext to decrypt. * @param {WordArray} key The key. * @param {Object} cfg (Optional) The configuration options to use for this operation. * * @return {WordArray} The plaintext. * * @static * * @example * * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL }); * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL }); */ decrypt: function (cipher, ciphertext, key, cfg) { // Apply config defaults cfg = this.cfg.extend(cfg); // Convert string to CipherParams ciphertext = this._parse(ciphertext, cfg.format); // Decrypt var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext); return plaintext; }, /** * Converts serialized ciphertext to CipherParams, * else assumed CipherParams already and returns ciphertext unchanged. * * @param {CipherParams|string} ciphertext The ciphertext. * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext. * * @return {CipherParams} The unserialized ciphertext. * * @static * * @example * * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format); */ _parse: function (ciphertext, format) { if (typeof ciphertext == 'string') { return format.parse(ciphertext, this); } else { return ciphertext; } } }); /** * Key derivation function namespace. */ var C_kdf = C.kdf = {}; /** * OpenSSL key derivation function. */ var OpenSSLKdf = C_kdf.OpenSSL = { /** * Derives a key and IV from a password. * * @param {string} password The password to derive from. * @param {number} keySize The size in words of the key to generate. * @param {number} ivSize The size in words of the IV to generate. * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly. * * @return {CipherParams} A cipher params object with the key, IV, and salt. * * @static * * @example * * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32); * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt'); */ execute: function (password, keySize, ivSize, salt) { // Generate random salt if (!salt) { salt = WordArray.random(64/8); } // Derive key and IV var key = EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt); // Separate key and IV var iv = WordArray.create(key.words.slice(keySize), ivSize * 4); key.sigBytes = keySize * 4; // Return params return CipherParams.create({ key: key, iv: iv, salt: salt }); } }; /** * A serializable cipher wrapper that derives the key from a password, * and returns ciphertext as a serializable cipher params object. */ var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({ /** * Configuration options. * * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL */ cfg: SerializableCipher.cfg.extend({ kdf: OpenSSLKdf }), /** * Encrypts a message using a password. * * @param {Cipher} cipher The cipher algorithm to use. * @param {WordArray|string} message The message to encrypt. * @param {string} password The password. * @param {Object} cfg (Optional) The configuration options to use for this operation. * * @return {CipherParams} A cipher params object. * * @static * * @example * * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password'); * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL }); */ encrypt: function (cipher, message, password, cfg) { // Apply config defaults cfg = this.cfg.extend(cfg); // Derive key and other params var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize); // Add IV to config cfg.iv = derivedParams.iv; // Encrypt var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg); // Mix in derived params ciphertext.mixIn(derivedParams); return ciphertext; }, /** * Decrypts serialized ciphertext using a password. * * @param {Cipher} cipher The cipher algorithm to use. * @param {CipherParams|string} ciphertext The ciphertext to decrypt. * @param {string} password The password. * @param {Object} cfg (Optional) The configuration options to use for this operation. * * @return {WordArray} The plaintext. * * @static * * @example * * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL }); * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL }); */ decrypt: function (cipher, ciphertext, password, cfg) { // Apply config defaults cfg = this.cfg.extend(cfg); // Convert string to CipherParams ciphertext = this._parse(ciphertext, cfg.format); // Derive key and other params var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt); // Add IV to config cfg.iv = derivedParams.iv; // Decrypt var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg); return plaintext; } }); }()); /** * Cipher Feedback block mode. */ CryptoJS.mode.CFB = (function () { var CFB = CryptoJS.lib.BlockCipherMode.extend(); CFB.Encryptor = CFB.extend({ processBlock: function (words, offset) { // Shortcuts var cipher = this._cipher; var blockSize = cipher.blockSize; generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); // Remember this block to use with next block this._prevBlock = words.slice(offset, offset + blockSize); } }); CFB.Decryptor = CFB.extend({ processBlock: function (words, offset) { // Shortcuts var cipher = this._cipher; var blockSize = cipher.blockSize; // Remember this block to use with next block var thisBlock = words.slice(offset, offset + blockSize); generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); // This block becomes the previous block this._prevBlock = thisBlock; } }); function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) { // Shortcut var iv = this._iv; // Generate keystream if (iv) { var keystream = iv.slice(0); // Remove IV for subsequent blocks this._iv = undefined; } else { var keystream = this._prevBlock; } cipher.encryptBlock(keystream, 0); // Encrypt for (var i = 0; i < blockSize; i++) { words[offset + i] ^= keystream[i]; } } return CFB; }()); /** * Electronic Codebook block mode. */ CryptoJS.mode.ECB = (function () { var ECB = CryptoJS.lib.BlockCipherMode.extend(); ECB.Encryptor = ECB.extend({ processBlock: function (words, offset) { this._cipher.encryptBlock(words, offset); } }); ECB.Decryptor = ECB.extend({ processBlock: function (words, offset) { this._cipher.decryptBlock(words, offset); } }); return ECB; }()); /** * ANSI X.923 padding strategy. */ CryptoJS.pad.AnsiX923 = { pad: function (data, blockSize) { // Shortcuts var dataSigBytes = data.sigBytes; var blockSizeBytes = blockSize * 4; // Count padding bytes var nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes; // Compute last byte position var lastBytePos = dataSigBytes + nPaddingBytes - 1; // Pad data.clamp(); data.words[lastBytePos >>> 2] |= nPaddingBytes << (24 - (lastBytePos % 4) * 8); data.sigBytes += nPaddingBytes; }, unpad: function (data) { // Get number of padding bytes from last byte var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; // Remove padding data.sigBytes -= nPaddingBytes; } }; /** * ISO 10126 padding strategy. */ CryptoJS.pad.Iso10126 = { pad: function (data, blockSize) { // Shortcut var blockSizeBytes = blockSize * 4; // Count padding bytes var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; // Pad data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes - 1)). concat(CryptoJS.lib.WordArray.create([nPaddingBytes << 24], 1)); }, unpad: function (data) { // Get number of padding bytes from last byte var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; // Remove padding data.sigBytes -= nPaddingBytes; } }; /** * ISO/IEC 9797-1 Padding Method 2. */ CryptoJS.pad.Iso97971 = { pad: function (data, blockSize) { // Add 0x80 byte data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1)); // Zero pad the rest CryptoJS.pad.ZeroPadding.pad(data, blockSize); }, unpad: function (data) { // Remove zero padding CryptoJS.pad.ZeroPadding.unpad(data); // Remove one more byte -- the 0x80 byte data.sigBytes--; } }; /** * Output Feedback block mode. */ CryptoJS.mode.OFB = (function () { var OFB = CryptoJS.lib.BlockCipherMode.extend(); var Encryptor = OFB.Encryptor = OFB.extend({ processBlock: function (words, offset) { // Shortcuts var cipher = this._cipher var blockSize = cipher.blockSize; var iv = this._iv; var keystream = this._keystream; // Generate keystream if (iv) { keystream = this._keystream = iv.slice(0); // Remove IV for subsequent blocks this._iv = undefined; } cipher.encryptBlock(keystream, 0); // Encrypt for (var i = 0; i < blockSize; i++) { words[offset + i] ^= keystream[i]; } } }); OFB.Decryptor = Encryptor; return OFB; }()); /** * A noop padding strategy. */ CryptoJS.pad.NoPadding = { pad: function () { }, unpad: function () { } }; (function (undefined) { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var CipherParams = C_lib.CipherParams; var C_enc = C.enc; var Hex = C_enc.Hex; var C_format = C.format; var HexFormatter = C_format.Hex = { /** * Converts the ciphertext of a cipher params object to a hexadecimally encoded string. * * @param {CipherParams} cipherParams The cipher params object. * * @return {string} The hexadecimally encoded string. * * @static * * @example * * var hexString = CryptoJS.format.Hex.stringify(cipherParams); */ stringify: function (cipherParams) { return cipherParams.ciphertext.toString(Hex); }, /** * Converts a hexadecimally encoded ciphertext string to a cipher params object. * * @param {string} input The hexadecimally encoded string. * * @return {CipherParams} The cipher params object. * * @static * * @example * * var cipherParams = CryptoJS.format.Hex.parse(hexString); */ parse: function (input) { var ciphertext = Hex.parse(input); return CipherParams.create({ ciphertext: ciphertext }); } }; }()); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var BlockCipher = C_lib.BlockCipher; var C_algo = C.algo; // Lookup tables var SBOX = []; var INV_SBOX = []; var SUB_MIX_0 = []; var SUB_MIX_1 = []; var SUB_MIX_2 = []; var SUB_MIX_3 = []; var INV_SUB_MIX_0 = []; var INV_SUB_MIX_1 = []; var INV_SUB_MIX_2 = []; var INV_SUB_MIX_3 = []; // Compute lookup tables (function () { // Compute double table var d = []; for (var i = 0; i < 256; i++) { if (i < 128) { d[i] = i << 1; } else { d[i] = (i << 1) ^ 0x11b; } } // Walk GF(2^8) var x = 0; var xi = 0; for (var i = 0; i < 256; i++) { // Compute sbox var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63; SBOX[x] = sx; INV_SBOX[sx] = x; // Compute multiplication var x2 = d[x]; var x4 = d[x2]; var x8 = d[x4]; // Compute sub bytes, mix columns tables var t = (d[sx] * 0x101) ^ (sx * 0x1010100); SUB_MIX_0[x] = (t << 24) | (t >>> 8); SUB_MIX_1[x] = (t << 16) | (t >>> 16); SUB_MIX_2[x] = (t << 8) | (t >>> 24); SUB_MIX_3[x] = t; // Compute inv sub bytes, inv mix columns tables var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100); INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8); INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16); INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24); INV_SUB_MIX_3[sx] = t; // Compute next counter if (!x) { x = xi = 1; } else { x = x2 ^ d[d[d[x8 ^ x2]]]; xi ^= d[d[xi]]; } } }()); // Precomputed Rcon lookup var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; /** * AES block cipher algorithm. */ var AES = C_algo.AES = BlockCipher.extend({ _doReset: function () { // Skip reset of nRounds has been set before and key did not change if (this._nRounds && this._keyPriorReset === this._key) { return; } // Shortcuts var key = this._keyPriorReset = this._key; var keyWords = key.words; var keySize = key.sigBytes / 4; // Compute number of rounds var nRounds = this._nRounds = keySize + 6; // Compute number of key schedule rows var ksRows = (nRounds + 1) * 4; // Compute key schedule var keySchedule = this._keySchedule = []; for (var ksRow = 0; ksRow < ksRows; ksRow++) { if (ksRow < keySize) { keySchedule[ksRow] = keyWords[ksRow]; } else { var t = keySchedule[ksRow - 1]; if (!(ksRow % keySize)) { // Rot word t = (t << 8) | (t >>> 24); // Sub word t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; // Mix Rcon t ^= RCON[(ksRow / keySize) | 0] << 24; } else if (keySize > 6 && ksRow % keySize == 4) { // Sub word t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; } keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t; } } // Compute inv key schedule var invKeySchedule = this._invKeySchedule = []; for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) { var ksRow = ksRows - invKsRow; if (invKsRow % 4) { var t = keySchedule[ksRow]; } else { var t = keySchedule[ksRow - 4]; } if (invKsRow < 4 || ksRow <= 4) { invKeySchedule[invKsRow] = t; } else { invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^ INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]]; } } }, encryptBlock: function (M, offset) { this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX); }, decryptBlock: function (M, offset) { // Swap 2nd and 4th rows var t = M[offset + 1]; M[offset + 1] = M[offset + 3]; M[offset + 3] = t; this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX); // Inv swap 2nd and 4th rows var t = M[offset + 1]; M[offset + 1] = M[offset + 3]; M[offset + 3] = t; }, _doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) { // Shortcut var nRounds = this._nRounds; // Get input, add round key var s0 = M[offset] ^ keySchedule[0]; var s1 = M[offset + 1] ^ keySchedule[1]; var s2 = M[offset + 2] ^ keySchedule[2]; var s3 = M[offset + 3] ^ keySchedule[3]; // Key schedule row counter var ksRow = 4; // Rounds for (var round = 1; round < nRounds; round++) { // Shift rows, sub bytes, mix columns, add round key var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++]; var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++]; var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++]; var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++]; // Update state s0 = t0; s1 = t1; s2 = t2; s3 = t3; } // Shift rows, sub bytes, add round key var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++]; var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++]; var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++]; var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++]; // Set output M[offset] = t0; M[offset + 1] = t1; M[offset + 2] = t2; M[offset + 3] = t3; }, keySize: 256/32 }); /** * Shortcut functions to the cipher's object interface. * * @example * * var ciphertext = CryptoJS.AES.encrypt(message, key, cfg); * var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg); */ C.AES = BlockCipher._createHelper(AES); }()); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var BlockCipher = C_lib.BlockCipher; var C_algo = C.algo; // Permuted Choice 1 constants var PC1 = [ 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 ]; // Permuted Choice 2 constants var PC2 = [ 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 ]; // Cumulative bit shift constants var BIT_SHIFTS = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28]; // SBOXes and round permutation constants var SBOX_P = [ { 0x0: 0x808200, 0x10000000: 0x8000, 0x20000000: 0x808002, 0x30000000: 0x2, 0x40000000: 0x200, 0x50000000: 0x808202, 0x60000000: 0x800202, 0x70000000: 0x800000, 0x80000000: 0x202, 0x90000000: 0x800200, 0xa0000000: 0x8200, 0xb0000000: 0x808000, 0xc0000000: 0x8002, 0xd0000000: 0x800002, 0xe0000000: 0x0, 0xf0000000: 0x8202, 0x8000000: 0x0, 0x18000000: 0x808202, 0x28000000: 0x8202, 0x38000000: 0x8000, 0x48000000: 0x808200, 0x58000000: 0x200, 0x68000000: 0x808002, 0x78000000: 0x2, 0x88000000: 0x800200, 0x98000000: 0x8200, 0xa8000000: 0x808000, 0xb8000000: 0x800202, 0xc8000000: 0x800002, 0xd8000000: 0x8002, 0xe8000000: 0x202, 0xf8000000: 0x800000, 0x1: 0x8000, 0x10000001: 0x2, 0x20000001: 0x808200, 0x30000001: 0x800000, 0x40000001: 0x808002, 0x50000001: 0x8200, 0x60000001: 0x200, 0x70000001: 0x800202, 0x80000001: 0x808202, 0x90000001: 0x808000, 0xa0000001: 0x800002, 0xb0000001: 0x8202, 0xc0000001: 0x202, 0xd0000001: 0x800200, 0xe0000001: 0x8002, 0xf0000001: 0x0, 0x8000001: 0x808202, 0x18000001: 0x808000, 0x28000001: 0x800000, 0x38000001: 0x200, 0x48000001: 0x8000, 0x58000001: 0x800002, 0x68000001: 0x2, 0x78000001: 0x8202, 0x88000001: 0x8002, 0x98000001: 0x800202, 0xa8000001: 0x202, 0xb8000001: 0x808200, 0xc8000001: 0x800200, 0xd8000001: 0x0, 0xe8000001: 0x8200, 0xf8000001: 0x808002 }, { 0x0: 0x40084010, 0x1000000: 0x4000, 0x2000000: 0x80000, 0x3000000: 0x40080010, 0x4000000: 0x40000010, 0x5000000: 0x40084000, 0x6000000: 0x40004000, 0x7000000: 0x10, 0x8000000: 0x84000, 0x9000000: 0x40004010, 0xa000000: 0x40000000, 0xb000000: 0x84010, 0xc000000: 0x80010, 0xd000000: 0x0, 0xe000000: 0x4010, 0xf000000: 0x40080000, 0x800000: 0x40004000, 0x1800000: 0x84010, 0x2800000: 0x10, 0x3800000: 0x40004010, 0x4800000: 0x40084010, 0x5800000: 0x40000000, 0x6800000: 0x80000, 0x7800000: 0x40080010, 0x8800000: 0x80010, 0x9800000: 0x0, 0xa800000: 0x4000, 0xb800000: 0x40080000, 0xc800000: 0x40000010, 0xd800000: 0x84000, 0xe800000: 0x40084000, 0xf800000: 0x4010, 0x10000000: 0x0, 0x11000000: 0x40080010, 0x12000000: 0x40004010, 0x13000000: 0x40084000, 0x14000000: 0x40080000, 0x15000000: 0x10, 0x16000000: 0x84010, 0x17000000: 0x4000, 0x18000000: 0x4010, 0x19000000: 0x80000, 0x1a000000: 0x80010, 0x1b000000: 0x40000010, 0x1c000000: 0x84000, 0x1d000000: 0x40004000, 0x1e000000: 0x40000000, 0x1f000000: 0x40084010, 0x10800000: 0x84010, 0x11800000: 0x80000, 0x12800000: 0x40080000, 0x13800000: 0x4000, 0x14800000: 0x40004000, 0x15800000: 0x40084010, 0x16800000: 0x10, 0x17800000: 0x40000000, 0x18800000: 0x40084000, 0x19800000: 0x40000010, 0x1a800000: 0x40004010, 0x1b800000: 0x80010, 0x1c800000: 0x0, 0x1d800000: 0x4010, 0x1e800000: 0x40080010, 0x1f800000: 0x84000 }, { 0x0: 0x104, 0x100000: 0x0, 0x200000: 0x4000100, 0x300000: 0x10104, 0x400000: 0x10004, 0x500000: 0x4000004, 0x600000: 0x4010104, 0x700000: 0x4010000, 0x800000: 0x4000000, 0x900000: 0x4010100, 0xa00000: 0x10100, 0xb00000: 0x4010004, 0xc00000: 0x4000104, 0xd00000: 0x10000, 0xe00000: 0x4, 0xf00000: 0x100, 0x80000: 0x4010100, 0x180000: 0x4010004, 0x280000: 0x0, 0x380000: 0x4000100, 0x480000: 0x4000004, 0x580000: 0x10000, 0x680000: 0x10004, 0x780000: 0x104, 0x880000: 0x4, 0x980000: 0x100, 0xa80000: 0x4010000, 0xb80000: 0x10104, 0xc80000: 0x10100, 0xd80000: 0x4000104, 0xe80000: 0x4010104, 0xf80000: 0x4000000, 0x1000000: 0x4010100, 0x1100000: 0x10004, 0x1200000: 0x10000, 0x1300000: 0x4000100, 0x1400000: 0x100, 0x1500000: 0x4010104, 0x1600000: 0x4000004, 0x1700000: 0x0, 0x1800000: 0x4000104, 0x1900000: 0x4000000, 0x1a00000: 0x4, 0x1b00000: 0x10100, 0x1c00000: 0x4010000, 0x1d00000: 0x104, 0x1e00000: 0x10104, 0x1f00000: 0x4010004, 0x1080000: 0x4000000, 0x1180000: 0x104, 0x1280000: 0x4010100, 0x1380000: 0x0, 0x1480000: 0x10004, 0x1580000: 0x4000100, 0x1680000: 0x100, 0x1780000: 0x4010004, 0x1880000: 0x10000, 0x1980000: 0x4010104, 0x1a80000: 0x10104, 0x1b80000: 0x4000004, 0x1c80000: 0x4000104, 0x1d80000: 0x4010000, 0x1e80000: 0x4, 0x1f80000: 0x10100 }, { 0x0: 0x80401000, 0x10000: 0x80001040, 0x20000: 0x401040, 0x30000: 0x80400000, 0x40000: 0x0, 0x50000: 0x401000, 0x60000: 0x80000040, 0x70000: 0x400040, 0x80000: 0x80000000, 0x90000: 0x400000, 0xa0000: 0x40, 0xb0000: 0x80001000, 0xc0000: 0x80400040, 0xd0000: 0x1040, 0xe0000: 0x1000, 0xf0000: 0x80401040, 0x8000: 0x80001040, 0x18000: 0x40, 0x28000: 0x80400040, 0x38000: 0x80001000, 0x48000: 0x401000, 0x58000: 0x80401040, 0x68000: 0x0, 0x78000: 0x80400000, 0x88000: 0x1000, 0x98000: 0x80401000, 0xa8000: 0x400000, 0xb8000: 0x1040, 0xc8000: 0x80000000, 0xd8000: 0x400040, 0xe8000: 0x401040, 0xf8000: 0x80000040, 0x100000: 0x400040, 0x110000: 0x401000, 0x120000: 0x80000040, 0x130000: 0x0, 0x140000: 0x1040, 0x150000: 0x80400040, 0x160000: 0x80401000, 0x170000: 0x80001040, 0x180000: 0x80401040, 0x190000: 0x80000000, 0x1a0000: 0x80400000, 0x1b0000: 0x401040, 0x1c0000: 0x80001000, 0x1d0000: 0x400000, 0x1e0000: 0x40, 0x1f0000: 0x1000, 0x108000: 0x80400000, 0x118000: 0x80401040, 0x128000: 0x0, 0x138000: 0x401000, 0x148000: 0x400040, 0x158000: 0x80000000, 0x168000: 0x80001040, 0x178000: 0x40, 0x188000: 0x80000040, 0x198000: 0x1000, 0x1a8000: 0x80001000, 0x1b8000: 0x80400040, 0x1c8000: 0x1040, 0x1d8000: 0x80401000, 0x1e8000: 0x400000, 0x1f8000: 0x401040 }, { 0x0: 0x80, 0x1000: 0x1040000, 0x2000: 0x40000, 0x3000: 0x20000000, 0x4000: 0x20040080, 0x5000: 0x1000080, 0x6000: 0x21000080, 0x7000: 0x40080, 0x8000: 0x1000000, 0x9000: 0x20040000, 0xa000: 0x20000080, 0xb000: 0x21040080, 0xc000: 0x21040000, 0xd000: 0x0, 0xe000: 0x1040080, 0xf000: 0x21000000, 0x800: 0x1040080, 0x1800: 0x21000080, 0x2800: 0x80, 0x3800: 0x1040000, 0x4800: 0x40000, 0x5800: 0x20040080, 0x6800: 0x21040000, 0x7800: 0x20000000, 0x8800: 0x20040000, 0x9800: 0x0, 0xa800: 0x21040080, 0xb800: 0x1000080, 0xc800: 0x20000080, 0xd800: 0x21000000, 0xe800: 0x1000000, 0xf800: 0x40080, 0x10000: 0x40000, 0x11000: 0x80, 0x12000: 0x20000000, 0x13000: 0x21000080, 0x14000: 0x1000080, 0x15000: 0x21040000, 0x16000: 0x20040080, 0x17000: 0x1000000, 0x18000: 0x21040080, 0x19000: 0x21000000, 0x1a000: 0x1040000, 0x1b000: 0x20040000, 0x1c000: 0x40080, 0x1d000: 0x20000080, 0x1e000: 0x0, 0x1f000: 0x1040080, 0x10800: 0x21000080, 0x11800: 0x1000000, 0x12800: 0x1040000, 0x13800: 0x20040080, 0x14800: 0x20000000, 0x15800: 0x1040080, 0x16800: 0x80, 0x17800: 0x21040000, 0x18800: 0x40080, 0x19800: 0x21040080, 0x1a800: 0x0, 0x1b800: 0x21000000, 0x1c800: 0x1000080, 0x1d800: 0x40000, 0x1e800: 0x20040000, 0x1f800: 0x20000080 }, { 0x0: 0x10000008, 0x100: 0x2000, 0x200: 0x10200000, 0x300: 0x10202008, 0x400: 0x10002000, 0x500: 0x200000, 0x600: 0x200008, 0x700: 0x10000000, 0x800: 0x0, 0x900: 0x10002008, 0xa00: 0x202000, 0xb00: 0x8, 0xc00: 0x10200008, 0xd00: 0x202008, 0xe00: 0x2008, 0xf00: 0x10202000, 0x80: 0x10200000, 0x180: 0x10202008, 0x280: 0x8, 0x380: 0x200000, 0x480: 0x202008, 0x580: 0x10000008, 0x680: 0x10002000, 0x780: 0x2008, 0x880: 0x200008, 0x980: 0x2000, 0xa80: 0x10002008, 0xb80: 0x10200008, 0xc80: 0x0, 0xd80: 0x10202000, 0xe80: 0x202000, 0xf80: 0x10000000, 0x1000: 0x10002000, 0x1100: 0x10200008, 0x1200: 0x10202008, 0x1300: 0x2008, 0x1400: 0x200000, 0x1500: 0x10000000, 0x1600: 0x10000008, 0x1700: 0x202000, 0x1800: 0x202008, 0x1900: 0x0, 0x1a00: 0x8, 0x1b00: 0x10200000, 0x1c00: 0x2000, 0x1d00: 0x10002008, 0x1e00: 0x10202000, 0x1f00: 0x200008, 0x1080: 0x8, 0x1180: 0x202000, 0x1280: 0x200000, 0x1380: 0x10000008, 0x1480: 0x10002000, 0x1580: 0x2008, 0x1680: 0x10202008, 0x1780: 0x10200000, 0x1880: 0x10202000, 0x1980: 0x10200008, 0x1a80: 0x2000, 0x1b80: 0x202008, 0x1c80: 0x200008, 0x1d80: 0x0, 0x1e80: 0x10000000, 0x1f80: 0x10002008 }, { 0x0: 0x100000, 0x10: 0x2000401, 0x20: 0x400, 0x30: 0x100401, 0x40: 0x2100401, 0x50: 0x0, 0x60: 0x1, 0x70: 0x2100001, 0x80: 0x2000400, 0x90: 0x100001, 0xa0: 0x2000001, 0xb0: 0x2100400, 0xc0: 0x2100000, 0xd0: 0x401, 0xe0: 0x100400, 0xf0: 0x2000000, 0x8: 0x2100001, 0x18: 0x0, 0x28: 0x2000401, 0x38: 0x2100400, 0x48: 0x100000, 0x58: 0x2000001, 0x68: 0x2000000, 0x78: 0x401, 0x88: 0x100401, 0x98: 0x2000400, 0xa8: 0x2100000, 0xb8: 0x100001, 0xc8: 0x400, 0xd8: 0x2100401, 0xe8: 0x1, 0xf8: 0x100400, 0x100: 0x2000000, 0x110: 0x100000, 0x120: 0x2000401, 0x130: 0x2100001, 0x140: 0x100001, 0x150: 0x2000400, 0x160: 0x2100400, 0x170: 0x100401, 0x180: 0x401, 0x190: 0x2100401, 0x1a0: 0x100400, 0x1b0: 0x1, 0x1c0: 0x0, 0x1d0: 0x2100000, 0x1e0: 0x2000001, 0x1f0: 0x400, 0x108: 0x100400, 0x118: 0x2000401, 0x128: 0x2100001, 0x138: 0x1, 0x148: 0x2000000, 0x158: 0x100000, 0x168: 0x401, 0x178: 0x2100400, 0x188: 0x2000001, 0x198: 0x2100000, 0x1a8: 0x0, 0x1b8: 0x2100401, 0x1c8: 0x100401, 0x1d8: 0x400, 0x1e8: 0x2000400, 0x1f8: 0x100001 }, { 0x0: 0x8000820, 0x1: 0x20000, 0x2: 0x8000000, 0x3: 0x20, 0x4: 0x20020, 0x5: 0x8020820, 0x6: 0x8020800, 0x7: 0x800, 0x8: 0x8020000, 0x9: 0x8000800, 0xa: 0x20800, 0xb: 0x8020020, 0xc: 0x820, 0xd: 0x0, 0xe: 0x8000020, 0xf: 0x20820, 0x80000000: 0x800, 0x80000001: 0x8020820, 0x80000002: 0x8000820, 0x80000003: 0x8000000, 0x80000004: 0x8020000, 0x80000005: 0x20800, 0x80000006: 0x20820, 0x80000007: 0x20, 0x80000008: 0x8000020, 0x80000009: 0x820, 0x8000000a: 0x20020, 0x8000000b: 0x8020800, 0x8000000c: 0x0, 0x8000000d: 0x8020020, 0x8000000e: 0x8000800, 0x8000000f: 0x20000, 0x10: 0x20820, 0x11: 0x8020800, 0x12: 0x20, 0x13: 0x800, 0x14: 0x8000800, 0x15: 0x8000020, 0x16: 0x8020020, 0x17: 0x20000, 0x18: 0x0, 0x19: 0x20020, 0x1a: 0x8020000, 0x1b: 0x8000820, 0x1c: 0x8020820, 0x1d: 0x20800, 0x1e: 0x820, 0x1f: 0x8000000, 0x80000010: 0x20000, 0x80000011: 0x800, 0x80000012: 0x8020020, 0x80000013: 0x20820, 0x80000014: 0x20, 0x80000015: 0x8020000, 0x80000016: 0x8000000, 0x80000017: 0x8000820, 0x80000018: 0x8020820, 0x80000019: 0x8000020, 0x8000001a: 0x8000800, 0x8000001b: 0x0, 0x8000001c: 0x20800, 0x8000001d: 0x820, 0x8000001e: 0x20020, 0x8000001f: 0x8020800 } ]; // Masks that select the SBOX input var SBOX_MASK = [ 0xf8000001, 0x1f800000, 0x01f80000, 0x001f8000, 0x0001f800, 0x00001f80, 0x000001f8, 0x8000001f ]; /** * DES block cipher algorithm. */ var DES = C_algo.DES = BlockCipher.extend({ _doReset: function () { // Shortcuts var key = this._key; var keyWords = key.words; // Select 56 bits according to PC1 var keyBits = []; for (var i = 0; i < 56; i++) { var keyBitPos = PC1[i] - 1; keyBits[i] = (keyWords[keyBitPos >>> 5] >>> (31 - keyBitPos % 32)) & 1; } // Assemble 16 subkeys var subKeys = this._subKeys = []; for (var nSubKey = 0; nSubKey < 16; nSubKey++) { // Create subkey var subKey = subKeys[nSubKey] = []; // Shortcut var bitShift = BIT_SHIFTS[nSubKey]; // Select 48 bits according to PC2 for (var i = 0; i < 24; i++) { // Select from the left 28 key bits subKey[(i / 6) | 0] |= keyBits[((PC2[i] - 1) + bitShift) % 28] << (31 - i % 6); // Select from the right 28 key bits subKey[4 + ((i / 6) | 0)] |= keyBits[28 + (((PC2[i + 24] - 1) + bitShift) % 28)] << (31 - i % 6); } // Since each subkey is applied to an expanded 32-bit input, // the subkey can be broken into 8 values scaled to 32-bits, // which allows the key to be used without expansion subKey[0] = (subKey[0] << 1) | (subKey[0] >>> 31); for (var i = 1; i < 7; i++) { subKey[i] = subKey[i] >>> ((i - 1) * 4 + 3); } subKey[7] = (subKey[7] << 5) | (subKey[7] >>> 27); } // Compute inverse subkeys var invSubKeys = this._invSubKeys = []; for (var i = 0; i < 16; i++) { invSubKeys[i] = subKeys[15 - i]; } }, encryptBlock: function (M, offset) { this._doCryptBlock(M, offset, this._subKeys); }, decryptBlock: function (M, offset) { this._doCryptBlock(M, offset, this._invSubKeys); }, _doCryptBlock: function (M, offset, subKeys) { // Get input this._lBlock = M[offset]; this._rBlock = M[offset + 1]; // Initial permutation exchangeLR.call(this, 4, 0x0f0f0f0f); exchangeLR.call(this, 16, 0x0000ffff); exchangeRL.call(this, 2, 0x33333333); exchangeRL.call(this, 8, 0x00ff00ff); exchangeLR.call(this, 1, 0x55555555); // Rounds for (var round = 0; round < 16; round++) { // Shortcuts var subKey = subKeys[round]; var lBlock = this._lBlock; var rBlock = this._rBlock; // Feistel function var f = 0; for (var i = 0; i < 8; i++) { f |= SBOX_P[i][((rBlock ^ subKey[i]) & SBOX_MASK[i]) >>> 0]; } this._lBlock = rBlock; this._rBlock = lBlock ^ f; } // Undo swap from last round var t = this._lBlock; this._lBlock = this._rBlock; this._rBlock = t; // Final permutation exchangeLR.call(this, 1, 0x55555555); exchangeRL.call(this, 8, 0x00ff00ff); exchangeRL.call(this, 2, 0x33333333); exchangeLR.call(this, 16, 0x0000ffff); exchangeLR.call(this, 4, 0x0f0f0f0f); // Set output M[offset] = this._lBlock; M[offset + 1] = this._rBlock; }, keySize: 64/32, ivSize: 64/32, blockSize: 64/32 }); // Swap bits across the left and right words function exchangeLR(offset, mask) { var t = ((this._lBlock >>> offset) ^ this._rBlock) & mask; this._rBlock ^= t; this._lBlock ^= t << offset; } function exchangeRL(offset, mask) { var t = ((this._rBlock >>> offset) ^ this._lBlock) & mask; this._lBlock ^= t; this._rBlock ^= t << offset; } /** * Shortcut functions to the cipher's object interface. * * @example * * var ciphertext = CryptoJS.DES.encrypt(message, key, cfg); * var plaintext = CryptoJS.DES.decrypt(ciphertext, key, cfg); */ C.DES = BlockCipher._createHelper(DES); /** * Triple-DES block cipher algorithm. */ var TripleDES = C_algo.TripleDES = BlockCipher.extend({ _doReset: function () { // Shortcuts var key = this._key; var keyWords = key.words; // Create DES instances this._des1 = DES.createEncryptor(WordArray.create(keyWords.slice(0, 2))); this._des2 = DES.createEncryptor(WordArray.create(keyWords.slice(2, 4))); this._des3 = DES.createEncryptor(WordArray.create(keyWords.slice(4, 6))); }, encryptBlock: function (M, offset) { this._des1.encryptBlock(M, offset); this._des2.decryptBlock(M, offset); this._des3.encryptBlock(M, offset); }, decryptBlock: function (M, offset) { this._des3.decryptBlock(M, offset); this._des2.encryptBlock(M, offset); this._des1.decryptBlock(M, offset); }, keySize: 192/32, ivSize: 64/32, blockSize: 64/32 }); /** * Shortcut functions to the cipher's object interface. * * @example * * var ciphertext = CryptoJS.TripleDES.encrypt(message, key, cfg); * var plaintext = CryptoJS.TripleDES.decrypt(ciphertext, key, cfg); */ C.TripleDES = BlockCipher._createHelper(TripleDES); }()); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var StreamCipher = C_lib.StreamCipher; var C_algo = C.algo; /** * RC4 stream cipher algorithm. */ var RC4 = C_algo.RC4 = StreamCipher.extend({ _doReset: function () { // Shortcuts var key = this._key; var keyWords = key.words; var keySigBytes = key.sigBytes; // Init sbox var S = this._S = []; for (var i = 0; i < 256; i++) { S[i] = i; } // Key setup for (var i = 0, j = 0; i < 256; i++) { var keyByteIndex = i % keySigBytes; var keyByte = (keyWords[keyByteIndex >>> 2] >>> (24 - (keyByteIndex % 4) * 8)) & 0xff; j = (j + S[i] + keyByte) % 256; // Swap var t = S[i]; S[i] = S[j]; S[j] = t; } // Counters this._i = this._j = 0; }, _doProcessBlock: function (M, offset) { M[offset] ^= generateKeystreamWord.call(this); }, keySize: 256/32, ivSize: 0 }); function generateKeystreamWord() { // Shortcuts var S = this._S; var i = this._i; var j = this._j; // Generate keystream word var keystreamWord = 0; for (var n = 0; n < 4; n++) { i = (i + 1) % 256; j = (j + S[i]) % 256; // Swap var t = S[i]; S[i] = S[j]; S[j] = t; keystreamWord |= S[(S[i] + S[j]) % 256] << (24 - n * 8); } // Update counters this._i = i; this._j = j; return keystreamWord; } /** * Shortcut functions to the cipher's object interface. * * @example * * var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg); * var plaintext = CryptoJS.RC4.decrypt(ciphertext, key, cfg); */ C.RC4 = StreamCipher._createHelper(RC4); /** * Modified RC4 stream cipher algorithm. */ var RC4Drop = C_algo.RC4Drop = RC4.extend({ /** * Configuration options. * * @property {number} drop The number of keystream words to drop. Default 192 */ cfg: RC4.cfg.extend({ drop: 192 }), _doReset: function () { RC4._doReset.call(this); // Drop for (var i = this.cfg.drop; i > 0; i--) { generateKeystreamWord.call(this); } } }); /** * Shortcut functions to the cipher's object interface. * * @example * * var ciphertext = CryptoJS.RC4Drop.encrypt(message, key, cfg); * var plaintext = CryptoJS.RC4Drop.decrypt(ciphertext, key, cfg); */ C.RC4Drop = StreamCipher._createHelper(RC4Drop); }()); /** @preserve * Counter block mode compatible with Dr Brian Gladman fileenc.c * derived from CryptoJS.mode.CTR * Jan Hruby jhruby.web@gmail.com */ CryptoJS.mode.CTRGladman = (function () { var CTRGladman = CryptoJS.lib.BlockCipherMode.extend(); function incWord(word) { if (((word >> 24) & 0xff) === 0xff) { //overflow var b1 = (word >> 16)&0xff; var b2 = (word >> 8)&0xff; var b3 = word & 0xff; if (b1 === 0xff) // overflow b1 { b1 = 0; if (b2 === 0xff) { b2 = 0; if (b3 === 0xff) { b3 = 0; } else { ++b3; } } else { ++b2; } } else { ++b1; } word = 0; word += (b1 << 16); word += (b2 << 8); word += b3; } else { word += (0x01 << 24); } return word; } function incCounter(counter) { if ((counter[0] = incWord(counter[0])) === 0) { // encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8 counter[1] = incWord(counter[1]); } return counter; } var Encryptor = CTRGladman.Encryptor = CTRGladman.extend({ processBlock: function (words, offset) { // Shortcuts var cipher = this._cipher var blockSize = cipher.blockSize; var iv = this._iv; var counter = this._counter; // Generate keystream if (iv) { counter = this._counter = iv.slice(0); // Remove IV for subsequent blocks this._iv = undefined; } incCounter(counter); var keystream = counter.slice(0); cipher.encryptBlock(keystream, 0); // Encrypt for (var i = 0; i < blockSize; i++) { words[offset + i] ^= keystream[i]; } } }); CTRGladman.Decryptor = Encryptor; return CTRGladman; }()); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var StreamCipher = C_lib.StreamCipher; var C_algo = C.algo; // Reusable objects var S = []; var C_ = []; var G = []; /** * Rabbit stream cipher algorithm */ var Rabbit = C_algo.Rabbit = StreamCipher.extend({ _doReset: function () { // Shortcuts var K = this._key.words; var iv = this.cfg.iv; // Swap endian for (var i = 0; i < 4; i++) { K[i] = (((K[i] << 8) | (K[i] >>> 24)) & 0x00ff00ff) | (((K[i] << 24) | (K[i] >>> 8)) & 0xff00ff00); } // Generate initial state values var X = this._X = [ K[0], (K[3] << 16) | (K[2] >>> 16), K[1], (K[0] << 16) | (K[3] >>> 16), K[2], (K[1] << 16) | (K[0] >>> 16), K[3], (K[2] << 16) | (K[1] >>> 16) ]; // Generate initial counter values var C = this._C = [ (K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff) ]; // Carry bit this._b = 0; // Iterate the system four times for (var i = 0; i < 4; i++) { nextState.call(this); } // Modify the counters for (var i = 0; i < 8; i++) { C[i] ^= X[(i + 4) & 7]; } // IV setup if (iv) { // Shortcuts var IV = iv.words; var IV_0 = IV[0]; var IV_1 = IV[1]; // Generate four subvectors var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00); var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00); var i1 = (i0 >>> 16) | (i2 & 0xffff0000); var i3 = (i2 << 16) | (i0 & 0x0000ffff); // Modify counter values C[0] ^= i0; C[1] ^= i1; C[2] ^= i2; C[3] ^= i3; C[4] ^= i0; C[5] ^= i1; C[6] ^= i2; C[7] ^= i3; // Iterate the system four times for (var i = 0; i < 4; i++) { nextState.call(this); } } }, _doProcessBlock: function (M, offset) { // Shortcut var X = this._X; // Iterate the system nextState.call(this); // Generate four keystream words S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16); S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16); S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16); S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16); for (var i = 0; i < 4; i++) { // Swap endian S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00); // Encrypt M[offset + i] ^= S[i]; } }, blockSize: 128/32, ivSize: 64/32 }); function nextState() { // Shortcuts var X = this._X; var C = this._C; // Save old counter values for (var i = 0; i < 8; i++) { C_[i] = C[i]; } // Calculate new counter values C[0] = (C[0] + 0x4d34d34d + this._b) | 0; C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0; C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0; C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0; C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0; C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0; C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0; C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0; this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0; // Calculate the g-values for (var i = 0; i < 8; i++) { var gx = X[i] + C[i]; // Construct high and low argument for squaring var ga = gx & 0xffff; var gb = gx >>> 16; // Calculate high and low result of squaring var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb; var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0); // High XOR low G[i] = gh ^ gl; } // Calculate new state values X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0; X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0; X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0; X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0; X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0; X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0; X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0; X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0; } /** * Shortcut functions to the cipher's object interface. * * @example * * var ciphertext = CryptoJS.Rabbit.encrypt(message, key, cfg); * var plaintext = CryptoJS.Rabbit.decrypt(ciphertext, key, cfg); */ C.Rabbit = StreamCipher._createHelper(Rabbit); }()); /** * Counter block mode. */ CryptoJS.mode.CTR = (function () { var CTR = CryptoJS.lib.BlockCipherMode.extend(); var Encryptor = CTR.Encryptor = CTR.extend({ processBlock: function (words, offset) { // Shortcuts var cipher = this._cipher var blockSize = cipher.blockSize; var iv = this._iv; var counter = this._counter; // Generate keystream if (iv) { counter = this._counter = iv.slice(0); // Remove IV for subsequent blocks this._iv = undefined; } var keystream = counter.slice(0); cipher.encryptBlock(keystream, 0); // Increment counter counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0 // Encrypt for (var i = 0; i < blockSize; i++) { words[offset + i] ^= keystream[i]; } } }); CTR.Decryptor = Encryptor; return CTR; }()); (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var StreamCipher = C_lib.StreamCipher; var C_algo = C.algo; // Reusable objects var S = []; var C_ = []; var G = []; /** * Rabbit stream cipher algorithm. * * This is a legacy version that neglected to convert the key to little-endian. * This error doesn't affect the cipher's security, * but it does affect its compatibility with other implementations. */ var RabbitLegacy = C_algo.RabbitLegacy = StreamCipher.extend({ _doReset: function () { // Shortcuts var K = this._key.words; var iv = this.cfg.iv; // Generate initial state values var X = this._X = [ K[0], (K[3] << 16) | (K[2] >>> 16), K[1], (K[0] << 16) | (K[3] >>> 16), K[2], (K[1] << 16) | (K[0] >>> 16), K[3], (K[2] << 16) | (K[1] >>> 16) ]; // Generate initial counter values var C = this._C = [ (K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff) ]; // Carry bit this._b = 0; // Iterate the system four times for (var i = 0; i < 4; i++) { nextState.call(this); } // Modify the counters for (var i = 0; i < 8; i++) { C[i] ^= X[(i + 4) & 7]; } // IV setup if (iv) { // Shortcuts var IV = iv.words; var IV_0 = IV[0]; var IV_1 = IV[1]; // Generate four subvectors var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00); var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00); var i1 = (i0 >>> 16) | (i2 & 0xffff0000); var i3 = (i2 << 16) | (i0 & 0x0000ffff); // Modify counter values C[0] ^= i0; C[1] ^= i1; C[2] ^= i2; C[3] ^= i3; C[4] ^= i0; C[5] ^= i1; C[6] ^= i2; C[7] ^= i3; // Iterate the system four times for (var i = 0; i < 4; i++) { nextState.call(this); } } }, _doProcessBlock: function (M, offset) { // Shortcut var X = this._X; // Iterate the system nextState.call(this); // Generate four keystream words S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16); S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16); S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16); S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16); for (var i = 0; i < 4; i++) { // Swap endian S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00); // Encrypt M[offset + i] ^= S[i]; } }, blockSize: 128/32, ivSize: 64/32 }); function nextState() { // Shortcuts var X = this._X; var C = this._C; // Save old counter values for (var i = 0; i < 8; i++) { C_[i] = C[i]; } // Calculate new counter values C[0] = (C[0] + 0x4d34d34d + this._b) | 0; C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0; C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0; C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0; C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0; C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0; C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0; C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0; this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0; // Calculate the g-values for (var i = 0; i < 8; i++) { var gx = X[i] + C[i]; // Construct high and low argument for squaring var ga = gx & 0xffff; var gb = gx >>> 16; // Calculate high and low result of squaring var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb; var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0); // High XOR low G[i] = gh ^ gl; } // Calculate new state values X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0; X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0; X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0; X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0; X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0; X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0; X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0; X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0; } /** * Shortcut functions to the cipher's object interface. * * @example * * var ciphertext = CryptoJS.RabbitLegacy.encrypt(message, key, cfg); * var plaintext = CryptoJS.RabbitLegacy.decrypt(ciphertext, key, cfg); */ C.RabbitLegacy = StreamCipher._createHelper(RabbitLegacy); }()); /** * Zero padding strategy. */ CryptoJS.pad.ZeroPadding = { pad: function (data, blockSize) { // Shortcut var blockSizeBytes = blockSize * 4; // Pad data.clamp(); data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes); }, unpad: function (data) { // Shortcut var dataWords = data.words; // Unpad var i = data.sigBytes - 1; while (!((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) { i--; } data.sigBytes = i + 1; } }; return CryptoJS; })); },{}],121:[function(require,module,exports){ 'use strict'; var elliptic = exports; // hello ladies ;) function inherits (ctor, superCtor) { ctor.super_ = superCtor; var TempCtor = function () {}; TempCtor.prototype = superCtor.prototype; ctor.prototype = new TempCtor(); ctor.prototype.constructor = ctor; } elliptic.inherits = inherits elliptic.version = '6.5.0'; elliptic.utils = require('./elliptic/utils'); elliptic.curve = require('./elliptic/curve'); elliptic.curves = require('./elliptic/curves'); // Protocols elliptic.ec = require('./elliptic/ec'); },{"./elliptic/curve":124,"./elliptic/curves":127,"./elliptic/ec":128,"./elliptic/utils":132}],122:[function(require,module,exports){ 'use strict'; var BN = require('../../../../BN/bn'); var elliptic = require('../../elliptic'); var utils = elliptic.utils; var getNAF = utils.getNAF; var getJSF = utils.getJSF; var assert = utils.assert; function BaseCurve(type, conf) { this.type = type; this.p = new BN(conf.p, 16); // Use Montgomery, when there is no fast reduction for the prime this.red = conf.prime ? BN.red(conf.prime) : BN.mont(this.p); // Useful for many curves this.zero = new BN(0).toRed(this.red); this.one = new BN(1).toRed(this.red); this.two = new BN(2).toRed(this.red); // Curve configuration, optional this.n = conf.n && new BN(conf.n, 16); this.g = conf.g && this.pointFromJSON(conf.g, conf.gRed); // Temporary arrays this._wnafT1 = new Array(4); this._wnafT2 = new Array(4); this._wnafT3 = new Array(4); this._wnafT4 = new Array(4); // Generalized Greg Maxwell's trick var adjustCount = this.n && this.p.div(this.n); if (!adjustCount || adjustCount.cmpn(100) > 0) { this.redN = null; } else { this._maxwellTrick = true; this.redN = this.n.toRed(this.red); } } module.exports = BaseCurve; BaseCurve.prototype.point = function point() { throw new Error('Not implemented'); }; BaseCurve.prototype.validate = function validate() { throw new Error('Not implemented'); }; BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) { assert(p.precomputed); var doubles = p._getDoubles(); var naf = getNAF(k, 1); var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0 ? 2 : 1); I /= 3; // Translate into more windowed form var repr = []; for (var j = 0; j < naf.length; j += doubles.step) { var nafW = 0; for (var k = j + doubles.step - 1; k >= j; k--) nafW = (nafW << 1) + naf[k]; repr.push(nafW); } var a = this.jpoint(null, null, null); var b = this.jpoint(null, null, null); for (var i = I; i > 0; i--) { for (var j = 0; j < repr.length; j++) { var nafW = repr[j]; if (nafW === i) b = b.mixedAdd(doubles.points[j]); else if (nafW === -i) b = b.mixedAdd(doubles.points[j].neg()); } a = a.add(b); } return a.toP(); }; BaseCurve.prototype._wnafMul = function _wnafMul(p, k) { var w = 4; // Precompute window var nafPoints = p._getNAFPoints(w); w = nafPoints.wnd; var wnd = nafPoints.points; // Get NAF form var naf = getNAF(k, w); // Add `this`*(N+1) for every w-NAF index var acc = this.jpoint(null, null, null); for (var i = naf.length - 1; i >= 0; i--) { // Count zeroes for (var k = 0; i >= 0 && naf[i] === 0; i--) k++; if (i >= 0) k++; acc = acc.dblp(k); if (i < 0) break; var z = naf[i]; assert(z !== 0); if (p.type === 'affine') { // J +- P if (z > 0) acc = acc.mixedAdd(wnd[(z - 1) >> 1]); else acc = acc.mixedAdd(wnd[(-z - 1) >> 1].neg()); } else { // J +- J if (z > 0) acc = acc.add(wnd[(z - 1) >> 1]); else acc = acc.add(wnd[(-z - 1) >> 1].neg()); } } return p.type === 'affine' ? acc.toP() : acc; }; BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW, points, coeffs, len, jacobianResult) { var wndWidth = this._wnafT1; var wnd = this._wnafT2; var naf = this._wnafT3; // Fill all arrays var max = 0; for (var i = 0; i < len; i++) { var p = points[i]; var nafPoints = p._getNAFPoints(defW); wndWidth[i] = nafPoints.wnd; wnd[i] = nafPoints.points; } // Comb small window NAFs for (var i = len - 1; i >= 1; i -= 2) { var a = i - 1; var b = i; if (wndWidth[a] !== 1 || wndWidth[b] !== 1) { naf[a] = getNAF(coeffs[a], wndWidth[a]); naf[b] = getNAF(coeffs[b], wndWidth[b]); max = Math.max(naf[a].length, max); max = Math.max(naf[b].length, max); continue; } var comb = [ points[a], /* 1 */ null, /* 3 */ null, /* 5 */ points[b] /* 7 */ ]; // Try to avoid Projective points, if possible if (points[a].y.cmp(points[b].y) === 0) { comb[1] = points[a].add(points[b]); comb[2] = points[a].toJ().mixedAdd(points[b].neg()); } else if (points[a].y.cmp(points[b].y.redNeg()) === 0) { comb[1] = points[a].toJ().mixedAdd(points[b]); comb[2] = points[a].add(points[b].neg()); } else { comb[1] = points[a].toJ().mixedAdd(points[b]); comb[2] = points[a].toJ().mixedAdd(points[b].neg()); } var index = [ -3, /* -1 -1 */ -1, /* -1 0 */ -5, /* -1 1 */ -7, /* 0 -1 */ 0, /* 0 0 */ 7, /* 0 1 */ 5, /* 1 -1 */ 1, /* 1 0 */ 3 /* 1 1 */ ]; var jsf = getJSF(coeffs[a], coeffs[b]); max = Math.max(jsf[0].length, max); naf[a] = new Array(max); naf[b] = new Array(max); for (var j = 0; j < max; j++) { var ja = jsf[0][j] | 0; var jb = jsf[1][j] | 0; naf[a][j] = index[(ja + 1) * 3 + (jb + 1)]; naf[b][j] = 0; wnd[a] = comb; } } var acc = this.jpoint(null, null, null); var tmp = this._wnafT4; for (var i = max; i >= 0; i--) { var k = 0; while (i >= 0) { var zero = true; for (var j = 0; j < len; j++) { tmp[j] = naf[j][i] | 0; if (tmp[j] !== 0) zero = false; } if (!zero) break; k++; i--; } if (i >= 0) k++; acc = acc.dblp(k); if (i < 0) break; for (var j = 0; j < len; j++) { var z = tmp[j]; var p; if (z === 0) continue; else if (z > 0) p = wnd[j][(z - 1) >> 1]; else if (z < 0) p = wnd[j][(-z - 1) >> 1].neg(); if (p.type === 'affine') acc = acc.mixedAdd(p); else acc = acc.add(p); } } // Zeroify references for (var i = 0; i < len; i++) wnd[i] = null; if (jacobianResult) return acc; else return acc.toP(); }; function BasePoint(curve, type) { this.curve = curve; this.type = type; this.precomputed = null; } BaseCurve.BasePoint = BasePoint; BasePoint.prototype.eq = function eq(/*other*/) { throw new Error('Not implemented'); }; BasePoint.prototype.validate = function validate() { return this.curve.validate(this); }; BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) { bytes = utils.toArray(bytes, enc); var len = this.p.byteLength(); // uncompressed, hybrid-odd, hybrid-even if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) && bytes.length - 1 === 2 * len) { if (bytes[0] === 0x06) assert(bytes[bytes.length - 1] % 2 === 0); else if (bytes[0] === 0x07) assert(bytes[bytes.length - 1] % 2 === 1); var res = this.point(bytes.slice(1, 1 + len), bytes.slice(1 + len, 1 + 2 * len)); return res; } else if ((bytes[0] === 0x02 || bytes[0] === 0x03) && bytes.length - 1 === len) { return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03); } throw new Error('Unknown point format'); }; BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) { return this.encode(enc, true); }; BasePoint.prototype._encode = function _encode(compact) { var len = this.curve.p.byteLength(); var x = this.getX().toArray('be', len); if (compact) return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x); return [ 0x04 ].concat(x, this.getY().toArray('be', len)) ; }; BasePoint.prototype.encode = function encode(enc, compact) { return utils.encode(this._encode(compact), enc); }; BasePoint.prototype.precompute = function precompute(power) { if (this.precomputed) return this; var precomputed = { doubles: null, naf: null, beta: null }; precomputed.naf = this._getNAFPoints(8); precomputed.doubles = this._getDoubles(4, power); precomputed.beta = this._getBeta(); this.precomputed = precomputed; return this; }; BasePoint.prototype._hasDoubles = function _hasDoubles(k) { if (!this.precomputed) return false; var doubles = this.precomputed.doubles; if (!doubles) return false; return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step); }; BasePoint.prototype._getDoubles = function _getDoubles(step, power) { if (this.precomputed && this.precomputed.doubles) return this.precomputed.doubles; var doubles = [ this ]; var acc = this; for (var i = 0; i < power; i += step) { for (var j = 0; j < step; j++) acc = acc.dbl(); doubles.push(acc); } return { step: step, points: doubles }; }; BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) { if (this.precomputed && this.precomputed.naf) return this.precomputed.naf; var res = [ this ]; var max = (1 << wnd) - 1; var dbl = max === 1 ? null : this.dbl(); for (var i = 1; i < max; i++) res[i] = res[i - 1].add(dbl); return { wnd: wnd, points: res }; }; BasePoint.prototype._getBeta = function _getBeta() { return null; }; BasePoint.prototype.dblp = function dblp(k) { var r = this; for (var i = 0; i < k; i++) r = r.dbl(); return r; }; },{"../../../../BN/bn":119,"../../elliptic":121}],123:[function(require,module,exports){ 'use strict'; var curve = require('../curve'); var elliptic = require('../../elliptic'); var BN = require('../../../../BN/bn'); var inherits = elliptic.inherits; var Base = curve.base; var assert = elliptic.utils.assert; function EdwardsCurve(conf) { // NOTE: Important as we are creating point in Base.call() this.twisted = (conf.a | 0) !== 1; this.mOneA = this.twisted && (conf.a | 0) === -1; this.extended = this.mOneA; Base.call(this, 'edwards', conf); this.a = new BN(conf.a, 16).umod(this.red.m); this.a = this.a.toRed(this.red); this.c = new BN(conf.c, 16).toRed(this.red); this.c2 = this.c.redSqr(); this.d = new BN(conf.d, 16).toRed(this.red); this.dd = this.d.redAdd(this.d); assert(!this.twisted || this.c.fromRed().cmpn(1) === 0); this.oneC = (conf.c | 0) === 1; } inherits(EdwardsCurve, Base); module.exports = EdwardsCurve; EdwardsCurve.prototype._mulA = function _mulA(num) { if (this.mOneA) return num.redNeg(); else return this.a.redMul(num); }; EdwardsCurve.prototype._mulC = function _mulC(num) { if (this.oneC) return num; else return this.c.redMul(num); }; // Just for compatibility with Short curve EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) { return this.point(x, y, z, t); }; EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) { x = new BN(x, 16); if (!x.red) x = x.toRed(this.red); var x2 = x.redSqr(); var rhs = this.c2.redSub(this.a.redMul(x2)); var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2)); var y2 = rhs.redMul(lhs.redInvm()); var y = y2.redSqrt(); if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) throw new Error('invalid point'); var isOdd = y.fromRed().isOdd(); if (odd && !isOdd || !odd && isOdd) y = y.redNeg(); return this.point(x, y); }; EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) { y = new BN(y, 16); if (!y.red) y = y.toRed(this.red); // x^2 = (y^2 - 1) / (d y^2 + 1) var y2 = y.redSqr(); var lhs = y2.redSub(this.one); var rhs = y2.redMul(this.d).redAdd(this.one); var x2 = lhs.redMul(rhs.redInvm()); if (x2.cmp(this.zero) === 0) { if (odd) throw new Error('invalid point'); else return this.point(this.zero, y); } var x = x2.redSqrt(); if (x.redSqr().redSub(x2).cmp(this.zero) !== 0) throw new Error('invalid point'); if (x.isOdd() !== odd) x = x.redNeg(); return this.point(x, y); }; EdwardsCurve.prototype.validate = function validate(point) { if (point.isInfinity()) return true; // Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2) point.normalize(); var x2 = point.x.redSqr(); var y2 = point.y.redSqr(); var lhs = x2.redMul(this.a).redAdd(y2); var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2))); return lhs.cmp(rhs) === 0; }; function Point(curve, x, y, z, t) { Base.BasePoint.call(this, curve, 'projective'); if (x === null && y === null && z === null) { this.x = this.curve.zero; this.y = this.curve.one; this.z = this.curve.one; this.t = this.curve.zero; this.zOne = true; } else { this.x = new BN(x, 16); this.y = new BN(y, 16); this.z = z ? new BN(z, 16) : this.curve.one; this.t = t && new BN(t, 16); if (!this.x.red) this.x = this.x.toRed(this.curve.red); if (!this.y.red) this.y = this.y.toRed(this.curve.red); if (!this.z.red) this.z = this.z.toRed(this.curve.red); if (this.t && !this.t.red) this.t = this.t.toRed(this.curve.red); this.zOne = this.z === this.curve.one; // Use extended coordinates if (this.curve.extended && !this.t) { this.t = this.x.redMul(this.y); if (!this.zOne) this.t = this.t.redMul(this.z.redInvm()); } } } inherits(Point, Base.BasePoint); EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) { return Point.fromJSON(this, obj); }; EdwardsCurve.prototype.point = function point(x, y, z, t) { return new Point(this, x, y, z, t); }; Point.fromJSON = function fromJSON(curve, obj) { return new Point(curve, obj[0], obj[1], obj[2]); }; Point.prototype.inspect = function inspect() { if (this.isInfinity()) return ''; return ''; }; Point.prototype.isInfinity = function isInfinity() { // XXX This code assumes that zero is always zero in red return this.x.cmpn(0) === 0 && this.y.cmp(this.z) === 0; }; Point.prototype._extDbl = function _extDbl() { // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html // #doubling-dbl-2008-hwcd // 4M + 4S // A = X1^2 var a = this.x.redSqr(); // B = Y1^2 var b = this.y.redSqr(); // C = 2 * Z1^2 var c = this.z.redSqr(); c = c.redIAdd(c); // D = a * A var d = this.curve._mulA(a); // E = (X1 + Y1)^2 - A - B var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b); // G = D + B var g = d.redAdd(b); // F = G - C var f = g.redSub(c); // H = D - B var h = d.redSub(b); // X3 = E * F var nx = e.redMul(f); // Y3 = G * H var ny = g.redMul(h); // T3 = E * H var nt = e.redMul(h); // Z3 = F * G var nz = f.redMul(g); return this.curve.point(nx, ny, nz, nt); }; Point.prototype._projDbl = function _projDbl() { // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html // #doubling-dbl-2008-bbjlp // #doubling-dbl-2007-bl // and others // Generally 3M + 4S or 2M + 4S // B = (X1 + Y1)^2 var b = this.x.redAdd(this.y).redSqr(); // C = X1^2 var c = this.x.redSqr(); // D = Y1^2 var d = this.y.redSqr(); var nx; var ny; var nz; if (this.curve.twisted) { // E = a * C var e = this.curve._mulA(c); // F = E + D var f = e.redAdd(d); if (this.zOne) { // X3 = (B - C - D) * (F - 2) nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two)); // Y3 = F * (E - D) ny = f.redMul(e.redSub(d)); // Z3 = F^2 - 2 * F nz = f.redSqr().redSub(f).redSub(f); } else { // H = Z1^2 var h = this.z.redSqr(); // J = F - 2 * H var j = f.redSub(h).redISub(h); // X3 = (B-C-D)*J nx = b.redSub(c).redISub(d).redMul(j); // Y3 = F * (E - D) ny = f.redMul(e.redSub(d)); // Z3 = F * J nz = f.redMul(j); } } else { // E = C + D var e = c.redAdd(d); // H = (c * Z1)^2 var h = this.curve._mulC(this.c.redMul(this.z)).redSqr(); // J = E - 2 * H var j = e.redSub(h).redSub(h); // X3 = c * (B - E) * J nx = this.curve._mulC(b.redISub(e)).redMul(j); // Y3 = c * E * (C - D) ny = this.curve._mulC(e).redMul(c.redISub(d)); // Z3 = E * J nz = e.redMul(j); } return this.curve.point(nx, ny, nz); }; Point.prototype.dbl = function dbl() { if (this.isInfinity()) return this; // Double in extended coordinates if (this.curve.extended) return this._extDbl(); else return this._projDbl(); }; Point.prototype._extAdd = function _extAdd(p) { // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html // #addition-add-2008-hwcd-3 // 8M // A = (Y1 - X1) * (Y2 - X2) var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x)); // B = (Y1 + X1) * (Y2 + X2) var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x)); // C = T1 * k * T2 var c = this.t.redMul(this.curve.dd).redMul(p.t); // D = Z1 * 2 * Z2 var d = this.z.redMul(p.z.redAdd(p.z)); // E = B - A var e = b.redSub(a); // F = D - C var f = d.redSub(c); // G = D + C var g = d.redAdd(c); // H = B + A var h = b.redAdd(a); // X3 = E * F var nx = e.redMul(f); // Y3 = G * H var ny = g.redMul(h); // T3 = E * H var nt = e.redMul(h); // Z3 = F * G var nz = f.redMul(g); return this.curve.point(nx, ny, nz, nt); }; Point.prototype._projAdd = function _projAdd(p) { // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html // #addition-add-2008-bbjlp // #addition-add-2007-bl // 10M + 1S // A = Z1 * Z2 var a = this.z.redMul(p.z); // B = A^2 var b = a.redSqr(); // C = X1 * X2 var c = this.x.redMul(p.x); // D = Y1 * Y2 var d = this.y.redMul(p.y); // E = d * C * D var e = this.curve.d.redMul(c).redMul(d); // F = B - E var f = b.redSub(e); // G = B + E var g = b.redAdd(e); // X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d); var nx = a.redMul(f).redMul(tmp); var ny; var nz; if (this.curve.twisted) { // Y3 = A * G * (D - a * C) ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c))); // Z3 = F * G nz = f.redMul(g); } else { // Y3 = A * G * (D - C) ny = a.redMul(g).redMul(d.redSub(c)); // Z3 = c * F * G nz = this.curve._mulC(f).redMul(g); } return this.curve.point(nx, ny, nz); }; Point.prototype.add = function add(p) { if (this.isInfinity()) return p; if (p.isInfinity()) return this; if (this.curve.extended) return this._extAdd(p); else return this._projAdd(p); }; Point.prototype.mul = function mul(k) { if (this._hasDoubles(k)) return this.curve._fixedNafMul(this, k); else return this.curve._wnafMul(this, k); }; Point.prototype.mulAdd = function mulAdd(k1, p, k2) { return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false); }; Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) { return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true); }; Point.prototype.normalize = function normalize() { if (this.zOne) return this; // Normalize coordinates var zi = this.z.redInvm(); this.x = this.x.redMul(zi); this.y = this.y.redMul(zi); if (this.t) this.t = this.t.redMul(zi); this.z = this.curve.one; this.zOne = true; return this; }; Point.prototype.neg = function neg() { return this.curve.point(this.x.redNeg(), this.y, this.z, this.t && this.t.redNeg()); }; Point.prototype.getX = function getX() { this.normalize(); return this.x.fromRed(); }; Point.prototype.getY = function getY() { this.normalize(); return this.y.fromRed(); }; Point.prototype.eq = function eq(other) { return this === other || this.getX().cmp(other.getX()) === 0 && this.getY().cmp(other.getY()) === 0; }; Point.prototype.eqXToP = function eqXToP(x) { var rx = x.toRed(this.curve.red).redMul(this.z); if (this.x.cmp(rx) === 0) return true; var xc = x.clone(); var t = this.curve.redN.redMul(this.z); for (;;) { xc.iadd(this.curve.n); if (xc.cmp(this.curve.p) >= 0) return false; rx.redIAdd(t); if (this.x.cmp(rx) === 0) return true; } return false; }; // Compatibility with BaseCurve Point.prototype.toP = Point.prototype.normalize; Point.prototype.mixedAdd = Point.prototype.add; },{"../../../../BN/bn":119,"../../elliptic":121,"../curve":124}],124:[function(require,module,exports){ 'use strict'; var curve = exports; curve.base = require('./base'); curve.short = require('./short'); curve.mont = require('./mont'); curve.edwards = require('./edwards'); },{"./base":122,"./edwards":123,"./mont":125,"./short":126}],125:[function(require,module,exports){ 'use strict'; var curve = require('../curve'); var BN = require('../../../../BN/bn'); var inherits = require('../../elliptic').inherits; var Base = curve.base; var elliptic = require('../../elliptic'); var utils = elliptic.utils; function MontCurve(conf) { Base.call(this, 'mont', conf); this.a = new BN(conf.a, 16).toRed(this.red); this.b = new BN(conf.b, 16).toRed(this.red); this.i4 = new BN(4).toRed(this.red).redInvm(); this.two = new BN(2).toRed(this.red); this.a24 = this.i4.redMul(this.a.redAdd(this.two)); } inherits(MontCurve, Base); module.exports = MontCurve; MontCurve.prototype.validate = function validate(point) { var x = point.normalize().x; var x2 = x.redSqr(); var rhs = x2.redMul(x).redAdd(x2.redMul(this.a)).redAdd(x); var y = rhs.redSqrt(); return y.redSqr().cmp(rhs) === 0; }; function Point(curve, x, z) { Base.BasePoint.call(this, curve, 'projective'); if (x === null && z === null) { this.x = this.curve.one; this.z = this.curve.zero; } else { this.x = new BN(x, 16); this.z = new BN(z, 16); if (!this.x.red) this.x = this.x.toRed(this.curve.red); if (!this.z.red) this.z = this.z.toRed(this.curve.red); } } inherits(Point, Base.BasePoint); MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) { return this.point(utils.toArray(bytes, enc), 1); }; MontCurve.prototype.point = function point(x, z) { return new Point(this, x, z); }; MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) { return Point.fromJSON(this, obj); }; Point.prototype.precompute = function precompute() { // No-op }; Point.prototype._encode = function _encode() { return this.getX().toArray('be', this.curve.p.byteLength()); }; Point.fromJSON = function fromJSON(curve, obj) { return new Point(curve, obj[0], obj[1] || curve.one); }; Point.prototype.inspect = function inspect() { if (this.isInfinity()) return ''; return ''; }; Point.prototype.isInfinity = function isInfinity() { // XXX This code assumes that zero is always zero in red return this.z.cmpn(0) === 0; }; Point.prototype.dbl = function dbl() { // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3 // 2M + 2S + 4A // A = X1 + Z1 var a = this.x.redAdd(this.z); // AA = A^2 var aa = a.redSqr(); // B = X1 - Z1 var b = this.x.redSub(this.z); // BB = B^2 var bb = b.redSqr(); // C = AA - BB var c = aa.redSub(bb); // X3 = AA * BB var nx = aa.redMul(bb); // Z3 = C * (BB + A24 * C) var nz = c.redMul(bb.redAdd(this.curve.a24.redMul(c))); return this.curve.point(nx, nz); }; Point.prototype.add = function add() { throw new Error('Not supported on Montgomery curve'); }; Point.prototype.diffAdd = function diffAdd(p, diff) { // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3 // 4M + 2S + 6A // A = X2 + Z2 var a = this.x.redAdd(this.z); // B = X2 - Z2 var b = this.x.redSub(this.z); // C = X3 + Z3 var c = p.x.redAdd(p.z); // D = X3 - Z3 var d = p.x.redSub(p.z); // DA = D * A var da = d.redMul(a); // CB = C * B var cb = c.redMul(b); // X5 = Z1 * (DA + CB)^2 var nx = diff.z.redMul(da.redAdd(cb).redSqr()); // Z5 = X1 * (DA - CB)^2 var nz = diff.x.redMul(da.redISub(cb).redSqr()); return this.curve.point(nx, nz); }; Point.prototype.mul = function mul(k) { var t = k.clone(); var a = this; // (N / 2) * Q + Q var b = this.curve.point(null, null); // (N / 2) * Q var c = this; // Q for (var bits = []; t.cmpn(0) !== 0; t.iushrn(1)) bits.push(t.andln(1)); for (var i = bits.length - 1; i >= 0; i--) { if (bits[i] === 0) { // N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q a = a.diffAdd(b, c); // N * Q = 2 * ((N / 2) * Q + Q)) b = b.dbl(); } else { // N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q) b = a.diffAdd(b, c); // N * Q + Q = 2 * ((N / 2) * Q + Q) a = a.dbl(); } } return b; }; Point.prototype.mulAdd = function mulAdd() { throw new Error('Not supported on Montgomery curve'); }; Point.prototype.jumlAdd = function jumlAdd() { throw new Error('Not supported on Montgomery curve'); }; Point.prototype.eq = function eq(other) { return this.getX().cmp(other.getX()) === 0; }; Point.prototype.normalize = function normalize() { this.x = this.x.redMul(this.z.redInvm()); this.z = this.curve.one; return this; }; Point.prototype.getX = function getX() { // Normalize coordinates this.normalize(); return this.x.fromRed(); }; },{"../../../../BN/bn":119,"../../elliptic":121,"../curve":124}],126:[function(require,module,exports){ 'use strict'; var curve = require('../curve'); var elliptic = require('../../elliptic'); var BN = require('../../../../BN/bn'); var inherits = elliptic.inherits; var Base = curve.base; var assert = elliptic.utils.assert; function ShortCurve(conf) { Base.call(this, 'short', conf); this.a = new BN(conf.a, 16).toRed(this.red); this.b = new BN(conf.b, 16).toRed(this.red); this.tinv = this.two.redInvm(); this.zeroA = this.a.fromRed().cmpn(0) === 0; this.threeA = this.a.fromRed().sub(this.p).cmpn(-3) === 0; // If the curve is endomorphic, precalculate beta and lambda this.endo = this._getEndomorphism(conf); this._endoWnafT1 = new Array(4); this._endoWnafT2 = new Array(4); } inherits(ShortCurve, Base); module.exports = ShortCurve; ShortCurve.prototype._getEndomorphism = function _getEndomorphism(conf) { // No efficient endomorphism if (!this.zeroA || !this.g || !this.n || this.p.modn(3) !== 1) return; // Compute beta and lambda, that lambda * P = (beta * Px; Py) var beta; var lambda; if (conf.beta) { beta = new BN(conf.beta, 16).toRed(this.red); } else { var betas = this._getEndoRoots(this.p); // Choose the smallest beta beta = betas[0].cmp(betas[1]) < 0 ? betas[0] : betas[1]; beta = beta.toRed(this.red); } if (conf.lambda) { lambda = new BN(conf.lambda, 16); } else { // Choose the lambda that is matching selected beta var lambdas = this._getEndoRoots(this.n); if (this.g.mul(lambdas[0]).x.cmp(this.g.x.redMul(beta)) === 0) { lambda = lambdas[0]; } else { lambda = lambdas[1]; assert(this.g.mul(lambda).x.cmp(this.g.x.redMul(beta)) === 0); } } // Get basis vectors, used for balanced length-two representation var basis; if (conf.basis) { basis = conf.basis.map(function(vec) { return { a: new BN(vec.a, 16), b: new BN(vec.b, 16) }; }); } else { basis = this._getEndoBasis(lambda); } return { beta: beta, lambda: lambda, basis: basis }; }; ShortCurve.prototype._getEndoRoots = function _getEndoRoots(num) { // Find roots of for x^2 + x + 1 in F // Root = (-1 +- Sqrt(-3)) / 2 // var red = num === this.p ? this.red : BN.mont(num); var tinv = new BN(2).toRed(red).redInvm(); var ntinv = tinv.redNeg(); var s = new BN(3).toRed(red).redNeg().redSqrt().redMul(tinv); var l1 = ntinv.redAdd(s).fromRed(); var l2 = ntinv.redSub(s).fromRed(); return [ l1, l2 ]; }; ShortCurve.prototype._getEndoBasis = function _getEndoBasis(lambda) { // aprxSqrt >= sqrt(this.n) var aprxSqrt = this.n.ushrn(Math.floor(this.n.bitLength() / 2)); // 3.74 // Run EGCD, until r(L + 1) < aprxSqrt var u = lambda; var v = this.n.clone(); var x1 = new BN(1); var y1 = new BN(0); var x2 = new BN(0); var y2 = new BN(1); // NOTE: all vectors are roots of: a + b * lambda = 0 (mod n) var a0; var b0; // First vector var a1; var b1; // Second vector var a2; var b2; var prevR; var i = 0; var r; var x; while (u.cmpn(0) !== 0) { var q = v.div(u); r = v.sub(q.mul(u)); x = x2.sub(q.mul(x1)); var y = y2.sub(q.mul(y1)); if (!a1 && r.cmp(aprxSqrt) < 0) { a0 = prevR.neg(); b0 = x1; a1 = r.neg(); b1 = x; } else if (a1 && ++i === 2) { break; } prevR = r; v = u; u = r; x2 = x1; x1 = x; y2 = y1; y1 = y; } a2 = r.neg(); b2 = x; var len1 = a1.sqr().add(b1.sqr()); var len2 = a2.sqr().add(b2.sqr()); if (len2.cmp(len1) >= 0) { a2 = a0; b2 = b0; } // Normalize signs if (a1.negative) { a1 = a1.neg(); b1 = b1.neg(); } if (a2.negative) { a2 = a2.neg(); b2 = b2.neg(); } return [ { a: a1, b: b1 }, { a: a2, b: b2 } ]; }; ShortCurve.prototype._endoSplit = function _endoSplit(k) { var basis = this.endo.basis; var v1 = basis[0]; var v2 = basis[1]; var c1 = v2.b.mul(k).divRound(this.n); var c2 = v1.b.neg().mul(k).divRound(this.n); var p1 = c1.mul(v1.a); var p2 = c2.mul(v2.a); var q1 = c1.mul(v1.b); var q2 = c2.mul(v2.b); // Calculate answer var k1 = k.sub(p1).sub(p2); var k2 = q1.add(q2).neg(); return { k1: k1, k2: k2 }; }; ShortCurve.prototype.pointFromX = function pointFromX(x, odd) { x = new BN(x, 16); if (!x.red) x = x.toRed(this.red); var y2 = x.redSqr().redMul(x).redIAdd(x.redMul(this.a)).redIAdd(this.b); var y = y2.redSqrt(); if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) throw new Error('invalid point'); // XXX Is there any way to tell if the number is odd without converting it // to non-red form? var isOdd = y.fromRed().isOdd(); if (odd && !isOdd || !odd && isOdd) y = y.redNeg(); return this.point(x, y); }; ShortCurve.prototype.validate = function validate(point) { if (point.inf) return true; var x = point.x; var y = point.y; var ax = this.a.redMul(x); var rhs = x.redSqr().redMul(x).redIAdd(ax).redIAdd(this.b); return y.redSqr().redISub(rhs).cmpn(0) === 0; }; ShortCurve.prototype._endoWnafMulAdd = function _endoWnafMulAdd(points, coeffs, jacobianResult) { var npoints = this._endoWnafT1; var ncoeffs = this._endoWnafT2; for (var i = 0; i < points.length; i++) { var split = this._endoSplit(coeffs[i]); var p = points[i]; var beta = p._getBeta(); if (split.k1.negative) { split.k1.ineg(); p = p.neg(true); } if (split.k2.negative) { split.k2.ineg(); beta = beta.neg(true); } npoints[i * 2] = p; npoints[i * 2 + 1] = beta; ncoeffs[i * 2] = split.k1; ncoeffs[i * 2 + 1] = split.k2; } var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2, jacobianResult); // Clean-up references to points and coefficients for (var j = 0; j < i * 2; j++) { npoints[j] = null; ncoeffs[j] = null; } return res; }; function Point(curve, x, y, isRed) { Base.BasePoint.call(this, curve, 'affine'); if (x === null && y === null) { this.x = null; this.y = null; this.inf = true; } else { this.x = new BN(x, 16); this.y = new BN(y, 16); // Force redgomery representation when loading from JSON if (isRed) { this.x.forceRed(this.curve.red); this.y.forceRed(this.curve.red); } if (!this.x.red) this.x = this.x.toRed(this.curve.red); if (!this.y.red) this.y = this.y.toRed(this.curve.red); this.inf = false; } } inherits(Point, Base.BasePoint); ShortCurve.prototype.point = function point(x, y, isRed) { return new Point(this, x, y, isRed); }; ShortCurve.prototype.pointFromJSON = function pointFromJSON(obj, red) { return Point.fromJSON(this, obj, red); }; Point.prototype._getBeta = function _getBeta() { if (!this.curve.endo) return; var pre = this.precomputed; if (pre && pre.beta) return pre.beta; var beta = this.curve.point(this.x.redMul(this.curve.endo.beta), this.y); if (pre) { var curve = this.curve; var endoMul = function(p) { return curve.point(p.x.redMul(curve.endo.beta), p.y); }; pre.beta = beta; beta.precomputed = { beta: null, naf: pre.naf && { wnd: pre.naf.wnd, points: pre.naf.points.map(endoMul) }, doubles: pre.doubles && { step: pre.doubles.step, points: pre.doubles.points.map(endoMul) } }; } return beta; }; Point.prototype.toJSON = function toJSON() { if (!this.precomputed) return [ this.x, this.y ]; return [ this.x, this.y, this.precomputed && { doubles: this.precomputed.doubles && { step: this.precomputed.doubles.step, points: this.precomputed.doubles.points.slice(1) }, naf: this.precomputed.naf && { wnd: this.precomputed.naf.wnd, points: this.precomputed.naf.points.slice(1) } } ]; }; Point.fromJSON = function fromJSON(curve, obj, red) { if (typeof obj === 'string') obj = JSON.parse(obj); var res = curve.point(obj[0], obj[1], red); if (!obj[2]) return res; function obj2point(obj) { return curve.point(obj[0], obj[1], red); } var pre = obj[2]; res.precomputed = { beta: null, doubles: pre.doubles && { step: pre.doubles.step, points: [ res ].concat(pre.doubles.points.map(obj2point)) }, naf: pre.naf && { wnd: pre.naf.wnd, points: [ res ].concat(pre.naf.points.map(obj2point)) } }; return res; }; Point.prototype.inspect = function inspect() { if (this.isInfinity()) return ''; return ''; }; Point.prototype.isInfinity = function isInfinity() { return this.inf; }; Point.prototype.add = function add(p) { // O + P = P if (this.inf) return p; // P + O = P if (p.inf) return this; // P + P = 2P if (this.eq(p)) return this.dbl(); // P + (-P) = O if (this.neg().eq(p)) return this.curve.point(null, null); // P + Q = O if (this.x.cmp(p.x) === 0) return this.curve.point(null, null); var c = this.y.redSub(p.y); if (c.cmpn(0) !== 0) c = c.redMul(this.x.redSub(p.x).redInvm()); var nx = c.redSqr().redISub(this.x).redISub(p.x); var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); return this.curve.point(nx, ny); }; Point.prototype.dbl = function dbl() { if (this.inf) return this; // 2P = O var ys1 = this.y.redAdd(this.y); if (ys1.cmpn(0) === 0) return this.curve.point(null, null); var a = this.curve.a; var x2 = this.x.redSqr(); var dyinv = ys1.redInvm(); var c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv); var nx = c.redSqr().redISub(this.x.redAdd(this.x)); var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); return this.curve.point(nx, ny); }; Point.prototype.getX = function getX() { return this.x.fromRed(); }; Point.prototype.getY = function getY() { return this.y.fromRed(); }; Point.prototype.mul = function mul(k) { k = new BN(k, 16); if (this._hasDoubles(k)) return this.curve._fixedNafMul(this, k); else if (this.curve.endo) return this.curve._endoWnafMulAdd([ this ], [ k ]); else return this.curve._wnafMul(this, k); }; Point.prototype.mulAdd = function mulAdd(k1, p2, k2) { var points = [ this, p2 ]; var coeffs = [ k1, k2 ]; if (this.curve.endo) return this.curve._endoWnafMulAdd(points, coeffs); else return this.curve._wnafMulAdd(1, points, coeffs, 2); }; Point.prototype.jmulAdd = function jmulAdd(k1, p2, k2) { var points = [ this, p2 ]; var coeffs = [ k1, k2 ]; if (this.curve.endo) return this.curve._endoWnafMulAdd(points, coeffs, true); else return this.curve._wnafMulAdd(1, points, coeffs, 2, true); }; Point.prototype.eq = function eq(p) { return this === p || this.inf === p.inf && (this.inf || this.x.cmp(p.x) === 0 && this.y.cmp(p.y) === 0); }; Point.prototype.neg = function neg(_precompute) { if (this.inf) return this; var res = this.curve.point(this.x, this.y.redNeg()); if (_precompute && this.precomputed) { var pre = this.precomputed; var negate = function(p) { return p.neg(); }; res.precomputed = { naf: pre.naf && { wnd: pre.naf.wnd, points: pre.naf.points.map(negate) }, doubles: pre.doubles && { step: pre.doubles.step, points: pre.doubles.points.map(negate) } }; } return res; }; Point.prototype.toJ = function toJ() { if (this.inf) return this.curve.jpoint(null, null, null); var res = this.curve.jpoint(this.x, this.y, this.curve.one); return res; }; function JPoint(curve, x, y, z) { Base.BasePoint.call(this, curve, 'jacobian'); if (x === null && y === null && z === null) { this.x = this.curve.one; this.y = this.curve.one; this.z = new BN(0); } else { this.x = new BN(x, 16); this.y = new BN(y, 16); this.z = new BN(z, 16); } if (!this.x.red) this.x = this.x.toRed(this.curve.red); if (!this.y.red) this.y = this.y.toRed(this.curve.red); if (!this.z.red) this.z = this.z.toRed(this.curve.red); this.zOne = this.z === this.curve.one; } inherits(JPoint, Base.BasePoint); ShortCurve.prototype.jpoint = function jpoint(x, y, z) { return new JPoint(this, x, y, z); }; JPoint.prototype.toP = function toP() { if (this.isInfinity()) return this.curve.point(null, null); var zinv = this.z.redInvm(); var zinv2 = zinv.redSqr(); var ax = this.x.redMul(zinv2); var ay = this.y.redMul(zinv2).redMul(zinv); return this.curve.point(ax, ay); }; JPoint.prototype.neg = function neg() { return this.curve.jpoint(this.x, this.y.redNeg(), this.z); }; JPoint.prototype.add = function add(p) { // O + P = P if (this.isInfinity()) return p; // P + O = P if (p.isInfinity()) return this; // 12M + 4S + 7A var pz2 = p.z.redSqr(); var z2 = this.z.redSqr(); var u1 = this.x.redMul(pz2); var u2 = p.x.redMul(z2); var s1 = this.y.redMul(pz2.redMul(p.z)); var s2 = p.y.redMul(z2.redMul(this.z)); var h = u1.redSub(u2); var r = s1.redSub(s2); if (h.cmpn(0) === 0) { if (r.cmpn(0) !== 0) return this.curve.jpoint(null, null, null); else return this.dbl(); } var h2 = h.redSqr(); var h3 = h2.redMul(h); var v = u1.redMul(h2); var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); var nz = this.z.redMul(p.z).redMul(h); return this.curve.jpoint(nx, ny, nz); }; JPoint.prototype.mixedAdd = function mixedAdd(p) { // O + P = P if (this.isInfinity()) return p.toJ(); // P + O = P if (p.isInfinity()) return this; // 8M + 3S + 7A var z2 = this.z.redSqr(); var u1 = this.x; var u2 = p.x.redMul(z2); var s1 = this.y; var s2 = p.y.redMul(z2).redMul(this.z); var h = u1.redSub(u2); var r = s1.redSub(s2); if (h.cmpn(0) === 0) { if (r.cmpn(0) !== 0) return this.curve.jpoint(null, null, null); else return this.dbl(); } var h2 = h.redSqr(); var h3 = h2.redMul(h); var v = u1.redMul(h2); var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); var nz = this.z.redMul(h); return this.curve.jpoint(nx, ny, nz); }; JPoint.prototype.dblp = function dblp(pow) { if (pow === 0) return this; if (this.isInfinity()) return this; if (!pow) return this.dbl(); if (this.curve.zeroA || this.curve.threeA) { var r = this; for (var i = 0; i < pow; i++) r = r.dbl(); return r; } // 1M + 2S + 1A + N * (4S + 5M + 8A) // N = 1 => 6M + 6S + 9A var a = this.curve.a; var tinv = this.curve.tinv; var jx = this.x; var jy = this.y; var jz = this.z; var jz4 = jz.redSqr().redSqr(); // Reuse results var jyd = jy.redAdd(jy); for (var i = 0; i < pow; i++) { var jx2 = jx.redSqr(); var jyd2 = jyd.redSqr(); var jyd4 = jyd2.redSqr(); var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); var t1 = jx.redMul(jyd2); var nx = c.redSqr().redISub(t1.redAdd(t1)); var t2 = t1.redISub(nx); var dny = c.redMul(t2); dny = dny.redIAdd(dny).redISub(jyd4); var nz = jyd.redMul(jz); if (i + 1 < pow) jz4 = jz4.redMul(jyd4); jx = nx; jz = nz; jyd = dny; } return this.curve.jpoint(jx, jyd.redMul(tinv), jz); }; JPoint.prototype.dbl = function dbl() { if (this.isInfinity()) return this; if (this.curve.zeroA) return this._zeroDbl(); else if (this.curve.threeA) return this._threeDbl(); else return this._dbl(); }; JPoint.prototype._zeroDbl = function _zeroDbl() { var nx; var ny; var nz; // Z = 1 if (this.zOne) { // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html // #doubling-mdbl-2007-bl // 1M + 5S + 14A // XX = X1^2 var xx = this.x.redSqr(); // YY = Y1^2 var yy = this.y.redSqr(); // YYYY = YY^2 var yyyy = yy.redSqr(); // S = 2 * ((X1 + YY)^2 - XX - YYYY) var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); s = s.redIAdd(s); // M = 3 * XX + a; a = 0 var m = xx.redAdd(xx).redIAdd(xx); // T = M ^ 2 - 2*S var t = m.redSqr().redISub(s).redISub(s); // 8 * YYYY var yyyy8 = yyyy.redIAdd(yyyy); yyyy8 = yyyy8.redIAdd(yyyy8); yyyy8 = yyyy8.redIAdd(yyyy8); // X3 = T nx = t; // Y3 = M * (S - T) - 8 * YYYY ny = m.redMul(s.redISub(t)).redISub(yyyy8); // Z3 = 2*Y1 nz = this.y.redAdd(this.y); } else { // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html // #doubling-dbl-2009-l // 2M + 5S + 13A // A = X1^2 var a = this.x.redSqr(); // B = Y1^2 var b = this.y.redSqr(); // C = B^2 var c = b.redSqr(); // D = 2 * ((X1 + B)^2 - A - C) var d = this.x.redAdd(b).redSqr().redISub(a).redISub(c); d = d.redIAdd(d); // E = 3 * A var e = a.redAdd(a).redIAdd(a); // F = E^2 var f = e.redSqr(); // 8 * C var c8 = c.redIAdd(c); c8 = c8.redIAdd(c8); c8 = c8.redIAdd(c8); // X3 = F - 2 * D nx = f.redISub(d).redISub(d); // Y3 = E * (D - X3) - 8 * C ny = e.redMul(d.redISub(nx)).redISub(c8); // Z3 = 2 * Y1 * Z1 nz = this.y.redMul(this.z); nz = nz.redIAdd(nz); } return this.curve.jpoint(nx, ny, nz); }; JPoint.prototype._threeDbl = function _threeDbl() { var nx; var ny; var nz; // Z = 1 if (this.zOne) { // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html // #doubling-mdbl-2007-bl // 1M + 5S + 15A // XX = X1^2 var xx = this.x.redSqr(); // YY = Y1^2 var yy = this.y.redSqr(); // YYYY = YY^2 var yyyy = yy.redSqr(); // S = 2 * ((X1 + YY)^2 - XX - YYYY) var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); s = s.redIAdd(s); // M = 3 * XX + a var m = xx.redAdd(xx).redIAdd(xx).redIAdd(this.curve.a); // T = M^2 - 2 * S var t = m.redSqr().redISub(s).redISub(s); // X3 = T nx = t; // Y3 = M * (S - T) - 8 * YYYY var yyyy8 = yyyy.redIAdd(yyyy); yyyy8 = yyyy8.redIAdd(yyyy8); yyyy8 = yyyy8.redIAdd(yyyy8); ny = m.redMul(s.redISub(t)).redISub(yyyy8); // Z3 = 2 * Y1 nz = this.y.redAdd(this.y); } else { // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b // 3M + 5S // delta = Z1^2 var delta = this.z.redSqr(); // gamma = Y1^2 var gamma = this.y.redSqr(); // beta = X1 * gamma var beta = this.x.redMul(gamma); // alpha = 3 * (X1 - delta) * (X1 + delta) var alpha = this.x.redSub(delta).redMul(this.x.redAdd(delta)); alpha = alpha.redAdd(alpha).redIAdd(alpha); // X3 = alpha^2 - 8 * beta var beta4 = beta.redIAdd(beta); beta4 = beta4.redIAdd(beta4); var beta8 = beta4.redAdd(beta4); nx = alpha.redSqr().redISub(beta8); // Z3 = (Y1 + Z1)^2 - gamma - delta nz = this.y.redAdd(this.z).redSqr().redISub(gamma).redISub(delta); // Y3 = alpha * (4 * beta - X3) - 8 * gamma^2 var ggamma8 = gamma.redSqr(); ggamma8 = ggamma8.redIAdd(ggamma8); ggamma8 = ggamma8.redIAdd(ggamma8); ggamma8 = ggamma8.redIAdd(ggamma8); ny = alpha.redMul(beta4.redISub(nx)).redISub(ggamma8); } return this.curve.jpoint(nx, ny, nz); }; JPoint.prototype._dbl = function _dbl() { var a = this.curve.a; // 4M + 6S + 10A var jx = this.x; var jy = this.y; var jz = this.z; var jz4 = jz.redSqr().redSqr(); var jx2 = jx.redSqr(); var jy2 = jy.redSqr(); var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); var jxd4 = jx.redAdd(jx); jxd4 = jxd4.redIAdd(jxd4); var t1 = jxd4.redMul(jy2); var nx = c.redSqr().redISub(t1.redAdd(t1)); var t2 = t1.redISub(nx); var jyd8 = jy2.redSqr(); jyd8 = jyd8.redIAdd(jyd8); jyd8 = jyd8.redIAdd(jyd8); jyd8 = jyd8.redIAdd(jyd8); var ny = c.redMul(t2).redISub(jyd8); var nz = jy.redAdd(jy).redMul(jz); return this.curve.jpoint(nx, ny, nz); }; JPoint.prototype.trpl = function trpl() { if (!this.curve.zeroA) return this.dbl().add(this); // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl // 5M + 10S + ... // XX = X1^2 var xx = this.x.redSqr(); // YY = Y1^2 var yy = this.y.redSqr(); // ZZ = Z1^2 var zz = this.z.redSqr(); // YYYY = YY^2 var yyyy = yy.redSqr(); // M = 3 * XX + a * ZZ2; a = 0 var m = xx.redAdd(xx).redIAdd(xx); // MM = M^2 var mm = m.redSqr(); // E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM var e = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); e = e.redIAdd(e); e = e.redAdd(e).redIAdd(e); e = e.redISub(mm); // EE = E^2 var ee = e.redSqr(); // T = 16*YYYY var t = yyyy.redIAdd(yyyy); t = t.redIAdd(t); t = t.redIAdd(t); t = t.redIAdd(t); // U = (M + E)^2 - MM - EE - T var u = m.redIAdd(e).redSqr().redISub(mm).redISub(ee).redISub(t); // X3 = 4 * (X1 * EE - 4 * YY * U) var yyu4 = yy.redMul(u); yyu4 = yyu4.redIAdd(yyu4); yyu4 = yyu4.redIAdd(yyu4); var nx = this.x.redMul(ee).redISub(yyu4); nx = nx.redIAdd(nx); nx = nx.redIAdd(nx); // Y3 = 8 * Y1 * (U * (T - U) - E * EE) var ny = this.y.redMul(u.redMul(t.redISub(u)).redISub(e.redMul(ee))); ny = ny.redIAdd(ny); ny = ny.redIAdd(ny); ny = ny.redIAdd(ny); // Z3 = (Z1 + E)^2 - ZZ - EE var nz = this.z.redAdd(e).redSqr().redISub(zz).redISub(ee); return this.curve.jpoint(nx, ny, nz); }; JPoint.prototype.mul = function mul(k, kbase) { k = new BN(k, kbase); return this.curve._wnafMul(this, k); }; JPoint.prototype.eq = function eq(p) { if (p.type === 'affine') return this.eq(p.toJ()); if (this === p) return true; // x1 * z2^2 == x2 * z1^2 var z2 = this.z.redSqr(); var pz2 = p.z.redSqr(); if (this.x.redMul(pz2).redISub(p.x.redMul(z2)).cmpn(0) !== 0) return false; // y1 * z2^3 == y2 * z1^3 var z3 = z2.redMul(this.z); var pz3 = pz2.redMul(p.z); return this.y.redMul(pz3).redISub(p.y.redMul(z3)).cmpn(0) === 0; }; JPoint.prototype.eqXToP = function eqXToP(x) { var zs = this.z.redSqr(); var rx = x.toRed(this.curve.red).redMul(zs); if (this.x.cmp(rx) === 0) return true; var xc = x.clone(); var t = this.curve.redN.redMul(zs); for (;;) { xc.iadd(this.curve.n); if (xc.cmp(this.curve.p) >= 0) return false; rx.redIAdd(t); if (this.x.cmp(rx) === 0) return true; } return false; }; JPoint.prototype.inspect = function inspect() { if (this.isInfinity()) return ''; return ''; }; JPoint.prototype.isInfinity = function isInfinity() { // XXX This code assumes that zero is always zero in red return this.z.cmpn(0) === 0; }; },{"../../../../BN/bn":119,"../../elliptic":121,"../curve":124}],127:[function(require,module,exports){ 'use strict'; var curves = exports; var elliptic = require('../elliptic'); var assert = elliptic.utils.assert; function PresetCurve(options) { if (options.type === 'short') this.curve = new elliptic.curve.short(options); else if (options.type === 'edwards') this.curve = new elliptic.curve.edwards(options); else this.curve = new elliptic.curve.mont(options); this.g = this.curve.g; this.n = this.curve.n; this.hash = options.hash; assert(this.g.validate(), 'Invalid curve'); assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, G*N != O'); } curves.PresetCurve = PresetCurve; function defineCurve(name, options) { Object.defineProperty(curves, name, { configurable: true, enumerable: true, get: function() { var curve = new PresetCurve(options); Object.defineProperty(curves, name, { configurable: true, enumerable: true, value: curve }); return curve; } }); } defineCurve('p192', { type: 'short', prime: 'p192', p: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff', a: 'ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc', b: '64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1', n: 'ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831', hash: 'SHA256', gRed: false, g: [ '188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012', '07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811' ] }); defineCurve('p224', { type: 'short', prime: 'p224', p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001', a: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe', b: 'b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4', n: 'ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d', hash: 'SHA256', gRed: false, g: [ 'b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21', 'bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34' ] }); defineCurve('p256', { type: 'short', prime: null, p: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff', a: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc', b: '5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b', n: 'ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551', hash: 'SHA256', gRed: false, g: [ '6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296', '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5' ] }); defineCurve('p384', { type: 'short', prime: null, p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 'fffffffe ffffffff 00000000 00000000 ffffffff', a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 'fffffffe ffffffff 00000000 00000000 fffffffc', b: 'b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f ' + '5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef', n: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 ' + 'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973', hash: 'SHA384', gRed: false, g: [ 'aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 ' + '5502f25d bf55296c 3a545e38 72760ab7', '3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 ' + '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f' ] }); defineCurve('p521', { type: 'short', prime: null, p: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 'ffffffff ffffffff ffffffff ffffffff ffffffff', a: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 'ffffffff ffffffff ffffffff ffffffff fffffffc', b: '00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b ' + '99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' + '3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00', n: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' + 'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409', hash: 'SHA512', gRed: false, g: [ '000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 ' + '053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 ' + 'a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66', '00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 ' + '579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 ' + '3fad0761 353c7086 a272c240 88be9476 9fd16650' ] }); defineCurve('curve25519', { type: 'mont', prime: 'p25519', p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', a: '76d06', b: '1', n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', hash: 'SHA256', gRed: false, g: [ '9' ] }); defineCurve('ed25519', { type: 'edwards', prime: 'p25519', p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', a: '-1', c: '1', // -121665 * (121666^(-1)) (mod P) d: '52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3', n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', hash: 'SHA256', gRed: false, g: [ '216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a', // 4/5 '6666666666666666666666666666666666666666666666666666666666666658' ] }); var pre; try { pre = require('./precomputed/secp256k1'); } catch (e) { pre = undefined; } defineCurve('secp256k1', { type: 'short', prime: 'k256', p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f', a: '0', b: '7', n: 'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141', h: '1', hash: 'SHA256', // Precomputed endomorphism beta: '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', lambda: '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', basis: [ { a: '3086d221a7d46bcde86c90e49284eb15', b: '-e4437ed6010e88286f547fa90abfe4c3' }, { a: '114ca50f7a8e2f3f657c1108d9d44cfd8', b: '3086d221a7d46bcde86c90e49284eb15' } ], gRed: false, g: [ '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', pre ] }); },{"../elliptic":121,"./precomputed/secp256k1":131}],128:[function(require,module,exports){ 'use strict'; var BN = require('../../../../BN/bn'); var HmacDRBG = require('../../hmac-drbg/hmac-drbg'); var elliptic = require('../../elliptic'); var utils = elliptic.utils; var assert = utils.assert; var KeyPair = require('./key'); var Signature = require('./signature'); function EC(options) { if (!(this instanceof EC)) return new EC(options); // Shortcut `elliptic.ec(curve-name)` if (typeof options === 'string') { assert(elliptic.curves.hasOwnProperty(options), 'Unknown curve ' + options); options = elliptic.curves[options]; } // Shortcut for `elliptic.ec(elliptic.curves.curveName)` if (options instanceof elliptic.curves.PresetCurve) options = { curve: options }; this.curve = options.curve.curve; this.n = this.curve.n; this.nh = this.n.ushrn(1); this.g = this.curve.g; // Point on curve this.g = options.curve.g; this.g.precompute(options.curve.n.bitLength() + 1); // Hash for function for DRBG this.hash = options.hash || options.curve.hash; } module.exports = EC; EC.prototype.keyPair = function keyPair(options) { return new KeyPair(this, options); }; EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) { return KeyPair.fromPrivate(this, priv, enc); }; EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) { return KeyPair.fromPublic(this, pub, enc); }; EC.prototype.genKeyPair = function genKeyPair(options) { if (!options) options = {}; // Instantiate Hmac_DRBG var drbg = new HmacDRBG({ hash: this.hash, pers: options.pers, persEnc: options.persEnc || 'utf8', entropy: options.entropy || null, //elliptic.rand(this.hash.hmacStrength), entropyEnc: options.entropy && options.entropyEnc || 'utf8', nonce: this.n.toArray() }); var bytes = this.n.byteLength(); var ns2 = this.n.sub(new BN(2)); do { var priv = new BN(drbg.generate(bytes)); if (priv.cmp(ns2) > 0) continue; priv.iaddn(1); return this.keyFromPrivate(priv); } while (true); }; EC.prototype._truncateToN = function truncateToN(msg, truncOnly) { var delta = msg.byteLength() * 8 - this.n.bitLength(); if (delta > 0) msg = msg.ushrn(delta); if (!truncOnly && msg.cmp(this.n) >= 0) return msg.sub(this.n); else return msg; }; EC.prototype.sign = function sign(msg, key, enc, options) { if (typeof enc === 'object') { options = enc; enc = null; } if (!options) options = {}; key = this.keyFromPrivate(key, enc); msg = this._truncateToN(new BN(msg, 16)); // Zero-extend key to provide enough entropy var bytes = this.n.byteLength(); var bkey = key.getPrivate().toArray('be', bytes); // Zero-extend nonce to have the same byte size as N var nonce = msg.toArray('be', bytes); // Instantiate Hmac_DRBG var drbg = new HmacDRBG({ hash: this.hash, entropy: options['extraEntropy'] ? bkey.concat (options['extraEntropy']) : bkey, nonce: nonce, pers: options.pers, persEnc: options.persEnc || 'utf8' }); // Number of bytes to generate var ns1 = this.n.sub(new BN(1)); for (var iter = 0; true; iter++) { var k = options.k ? options.k(iter) : new BN(drbg.generate(this.n.byteLength())); k = this._truncateToN(k, true); if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) continue; var kp = this.g.mul(k); if (kp.isInfinity()) continue; var kpX = kp.getX(); var r = kpX.umod(this.n); if (r.cmpn(0) === 0) continue; var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg)); s = s.umod(this.n); if (s.cmpn(0) === 0) continue; var recoveryParam = (kp.getY().isOdd() ? 1 : 0) | (kpX.cmp(r) !== 0 ? 2 : 0); // Use complement of `s`, if it is > `n / 2` if (options.canonical && s.cmp(this.nh) > 0) { s = this.n.sub(s); recoveryParam ^= 1; } return new Signature({ r: r, s: s, recoveryParam: recoveryParam }); } }; EC.prototype.verify = function verify(msg, signature, key, enc) { msg = this._truncateToN(new BN(msg, 16)); key = this.keyFromPublic(key, enc); signature = new Signature(signature, 'hex'); // Perform primitive values validation var r = signature.r; var s = signature.s; if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0) return false; if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0) return false; // Validate signature var sinv = s.invm(this.n); var u1 = sinv.mul(msg).umod(this.n); var u2 = sinv.mul(r).umod(this.n); if (!this.curve._maxwellTrick) { var p = this.g.mulAdd(u1, key.getPublic(), u2); if (p.isInfinity()) return false; return p.getX().umod(this.n).cmp(r) === 0; } // NOTE: Greg Maxwell's trick, inspired by: // https://git.io/vad3K var p = this.g.jmulAdd(u1, key.getPublic(), u2); if (p.isInfinity()) return false; // Compare `p.x` of Jacobian point with `r`, // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the // inverse of `p.z^2` return p.eqXToP(r); }; EC.prototype.recoverPubKey = function(msg, signature, j, enc) { assert((3 & j) === j, 'The recovery param is more than two bits'); signature = new Signature(signature, enc); var n = this.n; var e = new BN(msg); var r = signature.r; var s = signature.s; // A set LSB signifies that the y-coordinate is odd var isYOdd = j & 1; var isSecondKey = j >> 1; if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey) throw new Error('Unable to find sencond key candinate'); // 1.1. Let x = r + jn. if (isSecondKey) r = this.curve.pointFromX(r.add(this.curve.n), isYOdd); else r = this.curve.pointFromX(r, isYOdd); var rInv = signature.r.invm(n); var s1 = n.sub(e).mul(rInv).umod(n); var s2 = s.mul(rInv).umod(n); // 1.6.1 Compute Q = r^-1 (sR - eG) // Q = r^-1 (sR + -eG) return this.g.mulAdd(s1, r, s2); }; EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) { signature = new Signature(signature, enc); if (signature.recoveryParam !== null) return signature.recoveryParam; for (var i = 0; i < 4; i++) { var Qprime; try { Qprime = this.recoverPubKey(e, signature, i); } catch (e) { continue; } if (Qprime.eq(Q)) return i; } throw new Error('Unable to find valid recovery factor'); }; },{"../../../../BN/bn":119,"../../elliptic":121,"../../hmac-drbg/hmac-drbg":133,"./key":129,"./signature":130}],129:[function(require,module,exports){ 'use strict'; var BN = require('../../../../BN/bn'); var elliptic = require('../../elliptic'); var utils = elliptic.utils; var assert = utils.assert; function KeyPair(ec, options) { this.ec = ec; this.priv = null; this.pub = null; // KeyPair(ec, { priv: ..., pub: ... }) if (options.priv) this._importPrivate(options.priv, options.privEnc); if (options.pub) this._importPublic(options.pub, options.pubEnc); } module.exports = KeyPair; KeyPair.fromPublic = function fromPublic(ec, pub, enc) { if (pub instanceof KeyPair) return pub; return new KeyPair(ec, { pub: pub, pubEnc: enc }); }; KeyPair.fromPrivate = function fromPrivate(ec, priv, enc) { if (priv instanceof KeyPair) return priv; return new KeyPair(ec, { priv: priv, privEnc: enc }); }; KeyPair.prototype.validate = function validate() { var pub = this.getPublic(); if (pub.isInfinity()) return { result: false, reason: 'Invalid public key' }; if (!pub.validate()) return { result: false, reason: 'Public key is not a point' }; if (!pub.mul(this.ec.curve.n).isInfinity()) return { result: false, reason: 'Public key * N != O' }; return { result: true, reason: null }; }; KeyPair.prototype.getPublic = function getPublic(compact, enc) { // compact is optional argument if (typeof compact === 'string') { enc = compact; compact = null; } if (!this.pub) this.pub = this.ec.g.mul(this.priv); if (!enc) return this.pub; return this.pub.encode(enc, compact); }; KeyPair.prototype.getPrivate = function getPrivate(enc) { if (enc === 'hex') return this.priv.toString(16, 2); else return this.priv; }; KeyPair.prototype._importPrivate = function _importPrivate(key, enc) { this.priv = new BN(key, enc || 16); // Ensure that the priv won't be bigger than n, otherwise we may fail // in fixed multiplication method this.priv = this.priv.umod(this.ec.curve.n); }; KeyPair.prototype._importPublic = function _importPublic(key, enc) { if (key.x || key.y) { // Montgomery points only have an `x` coordinate. // Weierstrass/Edwards points on the other hand have both `x` and // `y` coordinates. if (this.ec.curve.type === 'mont') { assert(key.x, 'Need x coordinate'); } else if (this.ec.curve.type === 'short' || this.ec.curve.type === 'edwards') { assert(key.x && key.y, 'Need both x and y coordinate'); } this.pub = this.ec.curve.point(key.x, key.y); return; } this.pub = this.ec.curve.decodePoint(key, enc); }; // ECDH KeyPair.prototype.derive = function derive(pub) { return pub.mul(this.priv).getX(); }; // ECDSA KeyPair.prototype.sign = function sign(msg, enc, options) { return this.ec.sign(msg, this, enc, options); }; KeyPair.prototype.verify = function verify(msg, signature) { return this.ec.verify(msg, signature, this); }; KeyPair.prototype.inspect = function inspect() { return ''; }; },{"../../../../BN/bn":119,"../../elliptic":121}],130:[function(require,module,exports){ 'use strict'; var BN = require('../../../../BN/bn'); var elliptic = require('../../elliptic'); var utils = elliptic.utils; var assert = utils.assert; function Signature(options, enc) { if (options instanceof Signature) return options; //if (this._importDER(options, enc)) // return; assert(options.r && options.s, 'Signature without r or s'); this.r = new BN(options.r, 16); this.s = new BN(options.s, 16); if (options.recoveryParam === undefined) this.recoveryParam = null; else this.recoveryParam = options.recoveryParam; } module.exports = Signature; },{"../../../../BN/bn":119,"../../elliptic":121}],131:[function(require,module,exports){ module.exports = { doubles: { step: 4, points: [ [ 'e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a', 'f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821' ], [ '8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508', '11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf' ], [ '175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739', 'd3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695' ], [ '363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640', '4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9' ], [ '8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c', '4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36' ], [ '723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda', '96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f' ], [ 'eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa', '5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999' ], [ '100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0', 'cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09' ], [ 'e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d', '9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d' ], [ 'feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d', 'e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088' ], [ 'da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1', '9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d' ], [ '53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0', '5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8' ], [ '8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047', '10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a' ], [ '385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862', '283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453' ], [ '6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7', '7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160' ], [ '3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd', '56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0' ], [ '85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83', '7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6' ], [ '948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a', '53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589' ], [ '6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8', 'bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17' ], [ 'e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d', '4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda' ], [ 'e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725', '7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd' ], [ '213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754', '4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2' ], [ '4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c', '17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6' ], [ 'fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6', '6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f' ], [ '76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39', 'c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01' ], [ 'c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891', '893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3' ], [ 'd895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b', 'febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f' ], [ 'b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03', '2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7' ], [ 'e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d', 'eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78' ], [ 'a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070', '7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1' ], [ '90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4', 'e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150' ], [ '8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da', '662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82' ], [ 'e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11', '1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc' ], [ '8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e', 'efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b' ], [ 'e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41', '2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51' ], [ 'b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef', '67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45' ], [ 'd68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8', 'db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120' ], [ '324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d', '648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84' ], [ '4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96', '35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d' ], [ '9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd', 'ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d' ], [ '6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5', '9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8' ], [ 'a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266', '40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8' ], [ '7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71', '34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac' ], [ '928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac', 'c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f' ], [ '85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751', '1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962' ], [ 'ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e', '493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907' ], [ '827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241', 'c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec' ], [ 'eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3', 'be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d' ], [ 'e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f', '4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414' ], [ '1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19', 'aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd' ], [ '146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be', 'b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0' ], [ 'fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9', '6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811' ], [ 'da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2', '8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1' ], [ 'a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13', '7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c' ], [ '174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c', 'ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73' ], [ '959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba', '2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd' ], [ 'd2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151', 'e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405' ], [ '64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073', 'd99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589' ], [ '8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458', '38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e' ], [ '13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b', '69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27' ], [ 'bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366', 'd3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1' ], [ '8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa', '40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482' ], [ '8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0', '620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945' ], [ 'dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787', '7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573' ], [ 'f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e', 'ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82' ] ] }, naf: { wnd: 7, points: [ [ 'f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9', '388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672' ], [ '2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4', 'd8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6' ], [ '5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc', '6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da' ], [ 'acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe', 'cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37' ], [ '774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb', 'd984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b' ], [ 'f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8', 'ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81' ], [ 'd7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e', '581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58' ], [ 'defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34', '4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77' ], [ '2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c', '85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a' ], [ '352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5', '321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c' ], [ '2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f', '2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67' ], [ '9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714', '73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402' ], [ 'daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729', 'a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55' ], [ 'c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db', '2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482' ], [ '6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4', 'e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82' ], [ '1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5', 'b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396' ], [ '605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479', '2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49' ], [ '62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d', '80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf' ], [ '80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f', '1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a' ], [ '7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb', 'd0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7' ], [ 'd528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9', 'eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933' ], [ '49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963', '758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a' ], [ '77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74', '958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6' ], [ 'f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530', 'e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37' ], [ '463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b', '5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e' ], [ 'f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247', 'cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6' ], [ 'caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1', 'cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476' ], [ '2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120', '4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40' ], [ '7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435', '91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61' ], [ '754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18', '673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683' ], [ 'e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8', '59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5' ], [ '186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb', '3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b' ], [ 'df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f', '55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417' ], [ '5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143', 'efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868' ], [ '290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba', 'e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a' ], [ 'af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45', 'f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6' ], [ '766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a', '744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996' ], [ '59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e', 'c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e' ], [ 'f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8', 'e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d' ], [ '7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c', '30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2' ], [ '948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519', 'e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e' ], [ '7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab', '100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437' ], [ '3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca', 'ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311' ], [ 'd3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf', '8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4' ], [ '1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610', '68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575' ], [ '733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4', 'f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d' ], [ '15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c', 'd56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d' ], [ 'a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940', 'edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629' ], [ 'e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980', 'a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06' ], [ '311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3', '66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374' ], [ '34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf', '9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee' ], [ 'f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63', '4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1' ], [ 'd7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448', 'fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b' ], [ '32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf', '5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661' ], [ '7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5', '8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6' ], [ 'ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6', '8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e' ], [ '16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5', '5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d' ], [ 'eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99', 'f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc' ], [ '78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51', 'f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4' ], [ '494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5', '42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c' ], [ 'a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5', '204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b' ], [ 'c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997', '4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913' ], [ '841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881', '73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154' ], [ '5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5', '39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865' ], [ '36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66', 'd2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc' ], [ '336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726', 'ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224' ], [ '8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede', '6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e' ], [ '1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94', '60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6' ], [ '85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31', '3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511' ], [ '29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51', 'b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b' ], [ 'a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252', 'ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2' ], [ '4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5', 'cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c' ], [ 'd24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b', '6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3' ], [ 'ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4', '322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d' ], [ 'af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f', '6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700' ], [ 'e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889', '2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4' ], [ '591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246', 'b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196' ], [ '11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984', '998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4' ], [ '3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a', 'b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257' ], [ 'cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030', 'bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13' ], [ 'c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197', '6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096' ], [ 'c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593', 'c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38' ], [ 'a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef', '21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f' ], [ '347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38', '60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448' ], [ 'da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a', '49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a' ], [ 'c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111', '5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4' ], [ '4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502', '7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437' ], [ '3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea', 'be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7' ], [ 'cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26', '8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d' ], [ 'b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986', '39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a' ], [ 'd4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e', '62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54' ], [ '48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4', '25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77' ], [ 'dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda', 'ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517' ], [ '6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859', 'cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10' ], [ 'e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f', 'f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125' ], [ 'eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c', '6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e' ], [ '13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942', 'fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1' ], [ 'ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a', '1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2' ], [ 'b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80', '5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423' ], [ 'ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d', '438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8' ], [ '8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1', 'cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758' ], [ '52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63', 'c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375' ], [ 'e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352', '6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d' ], [ '7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193', 'ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec' ], [ '5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00', '9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0' ], [ '32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58', 'ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c' ], [ 'e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7', 'd3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4' ], [ '8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8', 'c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f' ], [ '4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e', '67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649' ], [ '3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d', 'cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826' ], [ '674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b', '299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5' ], [ 'd32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f', 'f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87' ], [ '30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6', '462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b' ], [ 'be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297', '62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc' ], [ '93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a', '7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c' ], [ 'b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c', 'ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f' ], [ 'd5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52', '4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a' ], [ 'd3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb', 'bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46' ], [ '463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065', 'bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f' ], [ '7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917', '603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03' ], [ '74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9', 'cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08' ], [ '30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3', '553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8' ], [ '9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57', '712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373' ], [ '176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66', 'ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3' ], [ '75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8', '9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8' ], [ '809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721', '9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1' ], [ '1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180', '4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9' ] ] } }; },{}],132:[function(require,module,exports){ 'use strict'; var utils = exports; var BN = require('../../../BN/bn'); utils.assert = function (condition, errorMessage) { if (!condition) { throw new Error(errorMessage) } }; // Represent num in a w-NAF form function getNAF(num, w) { var naf = []; var ws = 1 << (w + 1); var k = num.clone(); while (k.cmpn(1) >= 0) { var z; if (k.isOdd()) { var mod = k.andln(ws - 1); if (mod > (ws >> 1) - 1) z = (ws >> 1) - mod; else z = mod; k.isubn(z); } else { z = 0; } naf.push(z); // Optimization, shift by word if possible var shift = (k.cmpn(0) !== 0 && k.andln(ws - 1) === 0) ? (w + 1) : 1; for (var i = 1; i < shift; i++) naf.push(0); k.iushrn(shift); } return naf; } utils.getNAF = getNAF; // Represent k1, k2 in a Joint Sparse Form function getJSF(k1, k2) { var jsf = [ [], [] ]; k1 = k1.clone(); k2 = k2.clone(); var d1 = 0; var d2 = 0; while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) { // First phase var m14 = (k1.andln(3) + d1) & 3; var m24 = (k2.andln(3) + d2) & 3; if (m14 === 3) m14 = -1; if (m24 === 3) m24 = -1; var u1; if ((m14 & 1) === 0) { u1 = 0; } else { var m8 = (k1.andln(7) + d1) & 7; if ((m8 === 3 || m8 === 5) && m24 === 2) u1 = -m14; else u1 = m14; } jsf[0].push(u1); var u2; if ((m24 & 1) === 0) { u2 = 0; } else { var m8 = (k2.andln(7) + d2) & 7; if ((m8 === 3 || m8 === 5) && m14 === 2) u2 = -m24; else u2 = m24; } jsf[1].push(u2); // Second phase if (2 * d1 === u1 + 1) d1 = 1 - d1; if (2 * d2 === u2 + 1) d2 = 1 - d2; k1.iushrn(1); k2.iushrn(1); } return jsf; } utils.getJSF = getJSF; function cachedProperty(obj, name, computer) { var key = '_' + name; obj.prototype[name] = function cachedProperty() { return this[key] !== undefined ? this[key] : this[key] = computer.call(this); }; } utils.cachedProperty = cachedProperty; function parseBytes(bytes) { return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') : bytes; } utils.parseBytes = parseBytes; function intFromLE(bytes) { return new BN(bytes, 'hex', 'le'); } utils.intFromLE = intFromLE; // used to convert `CryptoJS` wordArrays into `crypto` hex buffers function wordToByteArray(word, length) { var ba = [], xFF = 0xFF; if (length > 0) ba.push(word >>> 24); if (length > 1) ba.push((word >>> 16) & xFF); if (length > 2) ba.push((word >>> 8) & xFF); if (length > 3) ba.push(word & xFF); return ba; } function wordArrayToBuffer(wordArray) { let length = undefined; if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) { length = wordArray.sigBytes; wordArray = wordArray.words; } else { throw Error('Argument not a wordArray') } const result = [] let bytes = [] let i = 0; while (length > 0) { bytes = wordToByteArray(wordArray[i], Math.min(4, length)); length -= bytes.length; result.push(bytes); i++; } return [].concat.apply([], result) } utils.wordArrayToBuffer = wordArrayToBuffer; },{"../../../BN/bn":119}],133:[function(require,module,exports){ 'use strict'; var CryptoJS = require('../../../crypto-js/crypto-js'); var assert = require('../elliptic/utils').assert; var utils = require ('../elliptic/utils') var { byteArrayToWordArray } = require('../../../../base/functions/encode'); // some static stuff const ONE = CryptoJS.enc.Utf8.parse ('\x01') const ZERO = CryptoJS.enc.Utf8.parse ('\x00') function HmacDRBG(options) { if (!(this instanceof HmacDRBG)) return new HmacDRBG(options); this.hash = options.hash this.predResist = !!options.predResist; this.outLen = this.hash.slice (3, 6); // only support SHAXXX hashes this.minEntropy = options.minEntropy || 192; this._reseed = null; this.reseedInterval = null; this.K = null; this.V = null; var entropy = options.entropy var nonce = options.nonce var pers = [] assert(entropy.length >= (this.minEntropy / 8), 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); this._init(entropy, nonce, pers); } module.exports = HmacDRBG; HmacDRBG.prototype._init = function init(entropy, nonce, pers) { var seed = entropy.concat(nonce).concat(pers); this.K = new CryptoJS.lib.WordArray.init () this.V = new CryptoJS.lib.WordArray.init () const magicNumber = (1 << 24) + (1 << 16) + (1 << 8) + 1 this.V.words = Array (this.outLen / 32).fill (magicNumber) this.V.sigBytes = 32 this._update(seed); this._reseed = 1; this.reseedInterval = 0x1000000000000; // 2^48 }; HmacDRBG.prototype._hmac = function hmac() { return new CryptoJS.lib.WordArray.init (); }; HmacDRBG.prototype._update = function update(seed) { var kmac = this._hmac () kmac.concat (this.V) kmac.concat (ZERO) if (seed) kmac.concat (byteArrayToWordArray (seed)); this.K = CryptoJS['Hmac' + this.hash] (kmac, this.K) this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) if (!seed) return; kmac = this._hmac () kmac.concat (this.V) kmac.concat (ONE) kmac.concat (byteArrayToWordArray (seed)) this.K = CryptoJS['Hmac' + this.hash] (kmac, this.K) this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) }; HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) { // Optional entropy enc if (typeof entropyEnc !== 'string') { addEnc = add; add = entropyEnc; entropyEnc = null; } //entropy = utils.toArray(entropy, entropyEnc); //add = utils.toArray(add, addEnc); assert(entropy.length >= (this.minEntropy / 8), 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); this._update(entropy.concat(add || [])); this._reseed = 1; }; HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) { if (this._reseed > this.reseedInterval) throw new Error ('Reseed is required'); // Optional encoding if (typeof enc !== 'string') { addEnc = add; add = enc; enc = null; } // Optional additional data if (add) { add = CryptoJS.enc.Utf8.parse (add); this._update (add); } var res = new CryptoJS.lib.WordArray.init (); while (res.sigBytes < len) { this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) res.concat (this.V); } this._update (add); this._reseed++; return utils.wordArrayToBuffer(res); }; },{"../../../../base/functions/encode":12,"../../../crypto-js/crypto-js":120,"../elliptic/utils":132}],134:[function(require,module,exports){ 'use strict'; var fetch = require('../node-fetch/index'); function wrapFetchForNode(fetch) { // Support schemaless URIs on the server for parity with the browser. // https://github.com/matthew-andrews/isomorphic-fetch/pull/10 const f = function (u, options) { if (typeof u === 'string' && u.slice(0, 2) === '//') { return fetch('https:' + u, options); } return fetch(u, options); }; // Igor Kroitor 2019 Dec 30 // expose http and https // https://github.com/ccxt/ccxt/issues/6327 f.http = fetch.http; f.https = fetch.https; return f; } module.exports = function (context) { // This modifies the global `node-fetch` object, which isn't great, since // different callers to `fetch-ponyfill` which pass a different Promise // implementation would each expect to have their implementation used. But, // given the way `node-fetch` is implemented, this is the only way to make // it work at all. if (context && context.Promise) { fetch.Promise = context.Promise; } return { fetch: wrapFetchForNode(fetch), Headers: fetch.Headers, Request: fetch.Request, Response: fetch.Response }; }; },{"../node-fetch/index":135}],135:[function(require,module,exports){ (function (global,Buffer){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js // (MIT licensed) const BUFFER = Symbol('buffer'); const TYPE = Symbol('type'); class Blob { constructor() { this[TYPE] = ''; const blobParts = arguments[0]; const options = arguments[1]; const buffers = []; if (blobParts) { const a = blobParts; const length = Number(a.length); for (let i = 0; i < length; i++) { const element = a[i]; let buffer; if (element instanceof Buffer) { buffer = element; } else if (ArrayBuffer.isView(element)) { buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); } else if (element instanceof ArrayBuffer) { buffer = Buffer.from(element); } else if (element instanceof Blob) { buffer = element[BUFFER]; } else { buffer = Buffer.from(typeof element === 'string' ? element : String(element)); } buffers.push(buffer); } } this[BUFFER] = Buffer.concat(buffers); let type = options && options.type !== undefined && String(options.type).toLowerCase(); if (type && !/[^\u0020-\u007E]/.test(type)) { this[TYPE] = type; } } get size() { return this[BUFFER].length; } get type() { return this[TYPE]; } slice() { const size = this.size; const start = arguments[0]; const end = arguments[1]; let relativeStart, relativeEnd; if (start === undefined) { relativeStart = 0; } else if (start < 0) { relativeStart = Math.max(size + start, 0); } else { relativeStart = Math.min(start, size); } if (end === undefined) { relativeEnd = size; } else if (end < 0) { relativeEnd = Math.max(size + end, 0); } else { relativeEnd = Math.min(end, size); } const span = Math.max(relativeEnd - relativeStart, 0); const buffer = this[BUFFER]; const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); const blob = new Blob([], { type: arguments[2] }); blob[BUFFER] = slicedBuffer; return blob; } } Object.defineProperties(Blob.prototype, { size: { enumerable: true }, type: { enumerable: true }, slice: { enumerable: true } }); Object.defineProperty(Blob.prototype, Symbol.toStringTag, { value: 'Blob', writable: false, enumerable: false, configurable: true }); /** * fetch-error.js * * FetchError interface for operational errors */ /** * Create FetchError instance * * @param String message Error message for human * @param String type Error type for machine * @param String systemError For Node.js system error * @return FetchError */ function FetchError(message, type, systemError) { Error.call(this, message); this.message = message; this.type = type; // when err.type is `system`, err.code contains system error code if (systemError) { this.code = this.errno = systemError.code; } // hide custom error implementation details from end-users Error.captureStackTrace(this, this.constructor); } FetchError.prototype = Object.create(Error.prototype); FetchError.prototype.constructor = FetchError; FetchError.prototype.name = 'FetchError'; /** * body.js * * Body interface provides common methods for Request and Response */ const Stream = require('stream'); var _require = require('stream'); const PassThrough = _require.PassThrough; /* let convert; try { convert = require('encoding').convert; } catch (e) {} */ const INTERNALS = Symbol('Body internals'); /** * Body mixin * * Ref: https://fetch.spec.whatwg.org/#body * * @param Stream body Readable stream * @param Object opts Response options * @return Void */ function Body(body) { var _this = this; var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref$size = _ref.size; let size = _ref$size === undefined ? 0 : _ref$size; var _ref$timeout = _ref.timeout; let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; if (body == null) { // body is undefined or null body = null; } else if (typeof body === 'string') { // body is string } else if (isURLSearchParams(body)) { // body is a URLSearchParams } else if (body instanceof Blob) { // body is blob } else if (Buffer.isBuffer(body)) { // body is buffer } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') { // body is array buffer } else if (body instanceof Stream) { // body is stream } else { // none of the above // coerce to string body = String(body); } this[INTERNALS] = { body, disturbed: false, error: null }; this.size = size; this.timeout = timeout; if (body instanceof Stream) { body.on('error', function (err) { _this[INTERNALS].error = new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err); }); } } Body.prototype = { get body() { return this[INTERNALS].body; }, get bodyUsed() { return this[INTERNALS].disturbed; }, /** * Decode response as ArrayBuffer * * @return Promise */ arrayBuffer() { return consumeBody.call(this).then(function (buf) { return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); }); }, /** * Return raw response as Blob * * @return Promise */ blob() { let ct = this.headers && this.headers.get('content-type') || ''; return consumeBody.call(this).then(function (buf) { return Object.assign( // Prevent copying new Blob([], { type: ct.toLowerCase() }), { [BUFFER]: buf }); }); }, /** * Decode response as json * * @return Promise */ json() { var _this2 = this; return consumeBody.call(this).then(function (buffer) { try { return JSON.parse(buffer.toString()); } catch (err) { return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json')); } }); }, /** * Decode response as text * * @return Promise */ text() { return consumeBody.call(this).then(function (buffer) { return buffer.toString(); }); }, /** * Decode response as buffer (non-spec api) * * @return Promise */ buffer() { return consumeBody.call(this); }, /** * Decode response as text, while automatically detecting the encoding and * trying to decode to UTF-8 (non-spec api) * * @return Promise */ textConverted() { var _this3 = this; return consumeBody.call(this).then(function (buffer) { return convertBody(buffer, _this3.headers); }); } }; // In browsers, all properties are enumerable. Object.defineProperties(Body.prototype, { body: { enumerable: true }, bodyUsed: { enumerable: true }, arrayBuffer: { enumerable: true }, blob: { enumerable: true }, json: { enumerable: true }, text: { enumerable: true } }); Body.mixIn = function (proto) { for (const name of Object.getOwnPropertyNames(Body.prototype)) { // istanbul ignore else: future proof if (!(name in proto)) { const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); Object.defineProperty(proto, name, desc); } } }; /** * Consume and convert an entire Body to a Buffer. * * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body * * @return Promise */ function consumeBody() { var _this4 = this; if (this[INTERNALS].disturbed) { return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`)); } this[INTERNALS].disturbed = true; if (this[INTERNALS].error) { return Body.Promise.reject(this[INTERNALS].error); } // body is null if (this.body === null) { return Body.Promise.resolve(Buffer.alloc(0)); } // body is string if (typeof this.body === 'string') { return Body.Promise.resolve(Buffer.from(this.body)); } // body is blob if (this.body instanceof Blob) { return Body.Promise.resolve(this.body[BUFFER]); } // body is buffer if (Buffer.isBuffer(this.body)) { return Body.Promise.resolve(this.body); } // body is buffer if (Object.prototype.toString.call(this.body) === '[object ArrayBuffer]') { return Body.Promise.resolve(Buffer.from(this.body)); } // istanbul ignore if: should never happen if (!(this.body instanceof Stream)) { return Body.Promise.resolve(Buffer.alloc(0)); } // body is stream // get ready to actually consume the body let accum = []; let accumBytes = 0; let abort = false; return new Body.Promise(function (resolve, reject) { let resTimeout; // allow timeout on slow response body if (_this4.timeout) { resTimeout = setTimeout(function () { abort = true; reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout')); }, _this4.timeout); } // handle stream error, such as incorrect content-encoding _this4.body.on('error', function (err) { reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err)); }); _this4.body.on('data', function (chunk) { if (abort || chunk === null) { return; } if (_this4.size && accumBytes + chunk.length > _this4.size) { abort = true; reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size')); return; } accumBytes += chunk.length; accum.push(chunk); }); _this4.body.on('end', function () { if (abort) { return; } clearTimeout(resTimeout); try { resolve(Buffer.concat(accum)); } catch (err) { // handle streams that have accumulated too much data (issue #414) reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err)); } }); }); } /** * Detect buffer encoding and convert to target encoding * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding * * @param Buffer buffer Incoming buffer * @param String encoding Target encoding * @return String */ function convertBody(buffer, headers) { if (typeof convert !== 'function') { throw new Error('The package `encoding` must be installed to use the textConverted() function'); } const ct = headers.get('content-type'); let charset = 'utf-8'; let res, str; // header if (ct) { res = /charset=([^;]*)/i.exec(ct); } // no charset in content type, peek at response body for at most 1024 bytes str = buffer.slice(0, 1024).toString(); // html5 if (!res && str) { res = / 0 && arguments[0] !== undefined ? arguments[0] : undefined; this[MAP] = Object.create(null); if (init instanceof Headers) { const rawHeaders = init.raw(); const headerNames = Object.keys(rawHeaders); for (const headerName of headerNames) { for (const value of rawHeaders[headerName]) { this.append(headerName, value); } } return; } // We don't worry about converting prop to ByteString here as append() // will handle it. if (init == null) { // no op } else if (typeof init === 'object') { const method = init[Symbol.iterator]; if (method != null) { if (typeof method !== 'function') { throw new TypeError('Header pairs must be iterable'); } // sequence> // Note: per spec we have to first exhaust the lists then process them const pairs = []; for (const pair of init) { if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') { throw new TypeError('Each header pair must be iterable'); } pairs.push(Array.from(pair)); } for (const pair of pairs) { if (pair.length !== 2) { throw new TypeError('Each header pair must be a name/value tuple'); } this.append(pair[0], pair[1]); } } else { // record for (const key of Object.keys(init)) { const value = init[key]; this.append(key, value); } } } else { throw new TypeError('Provided initializer must be an object'); } } /** * Return combined header value given name * * @param String name Header name * @return Mixed */ get(name) { name = `${name}`; validateName(name); const key = find(this[MAP], name); if (key === undefined) { return null; } return this[MAP][key].join(', '); } /** * Iterate over all headers * * @param Function callback Executed for each item with parameters (value, name, thisArg) * @param Boolean thisArg `this` context for callback function * @return Void */ forEach(callback) { let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; let pairs = getHeaders(this); let i = 0; while (i < pairs.length) { var _pairs$i = pairs[i]; const name = _pairs$i[0], value = _pairs$i[1]; callback.call(thisArg, value, name, this); pairs = getHeaders(this); i++; } } /** * Overwrite header values given name * * @param String name Header name * @param String value Header value * @return Void */ set(name, value) { name = `${name}`; value = `${value}`; validateName(name); validateValue(value); const key = find(this[MAP], name); this[MAP][key !== undefined ? key : name] = [value]; } /** * Append a value onto existing header * * @param String name Header name * @param String value Header value * @return Void */ append(name, value) { name = `${name}`; value = `${value}`; validateName(name); validateValue(value); const key = find(this[MAP], name); if (key !== undefined) { this[MAP][key].push(value); } else { this[MAP][name] = [value]; } } /** * Check for header name existence * * @param String name Header name * @return Boolean */ has(name) { name = `${name}`; validateName(name); return find(this[MAP], name) !== undefined; } /** * Delete all header values given name * * @param String name Header name * @return Void */ delete(name) { name = `${name}`; validateName(name); const key = find(this[MAP], name); if (key !== undefined) { delete this[MAP][key]; } } /** * Return raw headers (non-spec api) * * @return Object */ raw() { return this[MAP]; } /** * Get an iterator on keys. * * @return Iterator */ keys() { return createHeadersIterator(this, 'key'); } /** * Get an iterator on values. * * @return Iterator */ values() { return createHeadersIterator(this, 'value'); } /** * Get an iterator on entries. * * This is the default iterator of the Headers object. * * @return Iterator */ [Symbol.iterator]() { return createHeadersIterator(this, 'key+value'); } } Headers.prototype.entries = Headers.prototype[Symbol.iterator]; Object.defineProperty(Headers.prototype, Symbol.toStringTag, { value: 'Headers', writable: false, enumerable: false, configurable: true }); Object.defineProperties(Headers.prototype, { get: { enumerable: true }, forEach: { enumerable: true }, set: { enumerable: true }, append: { enumerable: true }, has: { enumerable: true }, delete: { enumerable: true }, keys: { enumerable: true }, values: { enumerable: true }, entries: { enumerable: true } }); function getHeaders(headers) { let kind = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'key+value'; const keys = Object.keys(headers[MAP]).sort(); return keys.map(kind === 'key' ? function (k) { return k.toLowerCase(); } : kind === 'value' ? function (k) { return headers[MAP][k].join(', '); } : function (k) { return [k.toLowerCase(), headers[MAP][k].join(', ')]; }); } const INTERNAL = Symbol('internal'); function createHeadersIterator(target, kind) { const iterator = Object.create(HeadersIteratorPrototype); iterator[INTERNAL] = { target, kind, index: 0 }; return iterator; } const HeadersIteratorPrototype = Object.setPrototypeOf({ next() { // istanbul ignore if if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { throw new TypeError('Value of `this` is not a HeadersIterator'); } var _INTERNAL = this[INTERNAL]; const target = _INTERNAL.target, kind = _INTERNAL.kind, index = _INTERNAL.index; const values = getHeaders(target, kind); const len = values.length; if (index >= len) { return { value: undefined, done: true }; } this[INTERNAL].index = index + 1; return { value: values[index], done: false }; } }, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { value: 'HeadersIterator', writable: false, enumerable: false, configurable: true }); /** * Export the Headers object in a form that Node.js can consume. * * @param Headers headers * @return Object */ function exportNodeCompatibleHeaders(headers) { const obj = Object.assign({ __proto__: null }, headers[MAP]); // http.request() only supports string as Host header. This hack makes // specifying custom Host header possible. const hostHeaderKey = find(headers[MAP], 'Host'); if (hostHeaderKey !== undefined) { obj[hostHeaderKey] = obj[hostHeaderKey][0]; } return obj; } /** * Create a Headers object from an object of headers, ignoring those that do * not conform to HTTP grammar productions. * * @param Object obj Object of headers * @return Headers */ function createHeadersLenient(obj) { const headers = new Headers(); for (const name of Object.keys(obj)) { if (invalidTokenRegex.test(name)) { continue; } if (Array.isArray(obj[name])) { for (const val of obj[name]) { if (invalidHeaderCharRegex.test(val)) { continue; } if (headers[MAP][name] === undefined) { headers[MAP][name] = [val]; } else { headers[MAP][name].push(val); } } } else if (!invalidHeaderCharRegex.test(obj[name])) { headers[MAP][name] = [obj[name]]; } } return headers; } /** * response.js * * Response class provides content decoding */ var _require$1 = require('http'); const STATUS_CODES = _require$1.STATUS_CODES; const INTERNALS$1 = Symbol('Response internals'); /** * Response class * * @param Stream body Readable stream * @param Object opts Response options * @return Void */ class Response { constructor() { let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; Body.call(this, body, opts); const status = opts.status || 200; this[INTERNALS$1] = { url: opts.url, status, statusText: opts.statusText || STATUS_CODES[status], headers: new Headers(opts.headers) }; } get url() { return this[INTERNALS$1].url; } get status() { return this[INTERNALS$1].status; } /** * Convenience property representing if the request ended normally */ get ok() { return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300; } get statusText() { return this[INTERNALS$1].statusText; } get headers() { return this[INTERNALS$1].headers; } /** * Clone this response * * @return Response */ clone() { return new Response(clone(this), { url: this.url, status: this.status, statusText: this.statusText, headers: this.headers, ok: this.ok }); } } Body.mixIn(Response.prototype); Object.defineProperties(Response.prototype, { url: { enumerable: true }, status: { enumerable: true }, ok: { enumerable: true }, statusText: { enumerable: true }, headers: { enumerable: true }, clone: { enumerable: true } }); Object.defineProperty(Response.prototype, Symbol.toStringTag, { value: 'Response', writable: false, enumerable: false, configurable: true }); /** * request.js * * Request class contains server only options * * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/. */ var _require$2 = require('url'); const format_url = _require$2.format; const parse_url = _require$2.parse; const INTERNALS$2 = Symbol('Request internals'); /** * Check if a value is an instance of Request. * * @param Mixed input * @return Boolean */ function isRequest(input) { return typeof input === 'object' && typeof input[INTERNALS$2] === 'object'; } /** * Request class * * @param Mixed input Url or Request instance * @param Object init Custom options * @return Void */ class Request { constructor(input) { let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; let parsedURL; // normalize input if (!isRequest(input)) { if (input && input.href) { // in order to support Node.js' Url objects; though WHATWG's URL objects // will fall into this branch also (since their `toString()` will return // `href` property anyway) parsedURL = parse_url(input.href); } else { // coerce input to a string before attempting to parse parsedURL = parse_url(`${input}`); } input = {}; } else { parsedURL = parse_url(input.url); } let method = init.method || input.method || 'GET'; method = method.toUpperCase(); if ((init.body != null || isRequest(input) && input.body !== null) && (method === 'GET' || method === 'HEAD')) { throw new TypeError('Request with GET/HEAD method cannot have body'); } let inputBody = init.body != null ? init.body : isRequest(input) && input.body !== null ? clone(input) : null; Body.call(this, inputBody, { timeout: init.timeout || input.timeout || 0, size: init.size || input.size || 0 }); const headers = new Headers(init.headers || input.headers || {}); if (init.body != null) { const contentType = extractContentType(this); if (contentType !== null && !headers.has('Content-Type')) { headers.append('Content-Type', contentType); } } this[INTERNALS$2] = { method, redirect: init.redirect || input.redirect || 'follow', headers, parsedURL }; // node-fetch-only options this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20; this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true; this.counter = init.counter || input.counter || 0; this.agent = init.agent || input.agent; } get method() { return this[INTERNALS$2].method; } get url() { return format_url(this[INTERNALS$2].parsedURL); } get headers() { return this[INTERNALS$2].headers; } get redirect() { return this[INTERNALS$2].redirect; } /** * Clone this request * * @return Request */ clone() { return new Request(this); } } Body.mixIn(Request.prototype); Object.defineProperty(Request.prototype, Symbol.toStringTag, { value: 'Request', writable: false, enumerable: false, configurable: true }); Object.defineProperties(Request.prototype, { method: { enumerable: true }, url: { enumerable: true }, headers: { enumerable: true }, redirect: { enumerable: true }, clone: { enumerable: true } }); /** * Convert a Request to Node.js http request options. * * @param Request A Request instance * @return Object The options object to be passed to http.request */ function getNodeRequestOptions(request) { const parsedURL = request[INTERNALS$2].parsedURL; const headers = new Headers(request[INTERNALS$2].headers); // fetch step 1.3 if (!headers.has('Accept')) { headers.set('Accept', '*/*'); } // Basic fetch if (!parsedURL.protocol || !parsedURL.hostname) { throw new TypeError('Only absolute URLs are supported'); } if (!/^https?:$/.test(parsedURL.protocol)) { throw new TypeError('Only HTTP(S) protocols are supported'); } // HTTP-network-or-cache fetch steps 2.4-2.7 let contentLengthValue = null; if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { contentLengthValue = '0'; } if (request.body != null) { const totalBytes = getTotalBytes(request); if (typeof totalBytes === 'number') { contentLengthValue = String(totalBytes); } } if (contentLengthValue) { headers.set('Content-Length', contentLengthValue); } // HTTP-network-or-cache fetch step 2.11 if (!headers.has('User-Agent')) { headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)'); } // HTTP-network-or-cache fetch step 2.15 if (request.compress) { headers.set('Accept-Encoding', 'gzip,deflate'); } if (!headers.has('Connection') && !request.agent) { headers.set('Connection', 'close'); } // HTTP-network fetch step 4.2 // chunked encoding is handled by Node.js return Object.assign({}, parsedURL, { method: request.method, headers: exportNodeCompatibleHeaders(headers), agent: request.agent }); } /** * index.js * * a request API compatible with window.fetch * * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/. */ const http = require('http'); const https = require('https'); var _require$3 = require('stream'); const PassThrough$1 = _require$3.PassThrough; var _require2 = require('url'); const resolve_url = _require2.resolve; const zlib = require('zlib'); /** * Fetch function * * @param Mixed url Absolute url or Request instance * @param Object opts Fetch options * @return Promise */ function fetch(url, opts) { // allow custom promise if (!fetch.Promise) { throw new Error('native promise missing, set fetch.Promise to your favorite alternative'); } Body.Promise = fetch.Promise; // wrap http.request into fetch return new fetch.Promise(function (resolve, reject) { // build request object const request = new Request(url, opts); const options = getNodeRequestOptions(request); const send = (options.protocol === 'https:' ? https : http).request; // send request const req = send(options); let reqTimeout; function finalize() { req.abort(); clearTimeout(reqTimeout); } if (request.timeout) { req.once('socket', function (socket) { reqTimeout = setTimeout(function () { reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout')); finalize(); }, request.timeout); }); } req.on('error', function (err) { reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); finalize(); }); req.on('response', function (res) { clearTimeout(reqTimeout); const headers = createHeadersLenient(res.headers); // HTTP fetch step 5 if (fetch.isRedirect(res.statusCode)) { // HTTP fetch step 5.2 const location = headers.get('Location'); // HTTP fetch step 5.3 const locationURL = location === null ? null : resolve_url(request.url, location); // HTTP fetch step 5.5 switch (request.redirect) { case 'error': reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); finalize(); return; case 'manual': // node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL. if (locationURL !== null) { headers.set('Location', locationURL); } break; case 'follow': // HTTP-redirect fetch step 2 if (locationURL === null) { break; } // HTTP-redirect fetch step 5 if (request.counter >= request.follow) { reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); finalize(); return; } // HTTP-redirect fetch step 6 (counter increment) // Create a new Request object. const requestOpts = { headers: new Headers(request.headers), follow: request.follow, counter: request.counter + 1, agent: request.agent, compress: request.compress, method: request.method, body: request.body }; // HTTP-redirect fetch step 9 if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) { reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')); finalize(); return; } // HTTP-redirect fetch step 11 if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { requestOpts.method = 'GET'; requestOpts.body = undefined; requestOpts.headers.delete('content-length'); } // HTTP-redirect fetch step 15 resolve(fetch(new Request(locationURL, requestOpts))); finalize(); return; } } // prepare response let body = res.pipe(new PassThrough$1()); const response_options = { url: request.url, status: res.statusCode, statusText: res.statusMessage, headers: headers, size: request.size, timeout: request.timeout }; // HTTP-network fetch step 12.1.1.3 const codings = headers.get('Content-Encoding'); // HTTP-network fetch step 12.1.1.4: handle content codings // in following scenarios we ignore compression support // 1. compression support is disabled // 2. HEAD request // 3. no Content-Encoding header // 4. no content response (204) // 5. content not modified response (304) if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { resolve(new Response(body, response_options)); return; } // For Node v6+ // Be less strict when decoding compressed responses, since sometimes // servers send slightly invalid responses that are still accepted // by common browsers. // Always using Z_SYNC_FLUSH is what cURL does. const zlibOptions = { flush: zlib.Z_SYNC_FLUSH, finishFlush: zlib.Z_SYNC_FLUSH }; // for gzip if (codings == 'gzip' || codings == 'x-gzip') { body = body.pipe(zlib.createGunzip(zlibOptions)); resolve(new Response(body, response_options)); return; } // for deflate if (codings == 'deflate' || codings == 'x-deflate') { // handle the infamous raw deflate response from old servers // a hack for old IIS and Apache servers const raw = res.pipe(new PassThrough$1()); raw.once('data', function (chunk) { // see http://stackoverflow.com/questions/37519828 if ((chunk[0] & 0x0F) === 0x08) { body = body.pipe(zlib.createInflate()); } else { body = body.pipe(zlib.createInflateRaw()); } resolve(new Response(body, response_options)); }); return; } // otherwise, use response as-is resolve(new Response(body, response_options)); }); writeToStream(req, request); }); } /** * Redirect code matching * * @param Number code Status code * @return Boolean */ fetch.isRedirect = function (code) { return code === 301 || code === 302 || code === 303 || code === 307 || code === 308; }; // Needed for TypeScript. fetch.default = fetch; // expose Promise fetch.Promise = global.Promise; // Igor Kroitor 2019 Dec 30 // expose http and https // https://github.com/ccxt/ccxt/issues/6327 fetch.http = http fetch.https = https module.exports = exports = fetch; exports.Headers = Headers; exports.Request = Request; exports.Response = Response; exports.FetchError = FetchError; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) },{"buffer":178,"http":220,"https":183,"stream":219,"url":226,"zlib":177}],136:[function(require,module,exports){ (function (Buffer){ /*! * RSA library for Node.js * * Author: rzcoder * License MIT */ var rsa = require('./libs/rsa.js'); var _ = require('./utils')._; var schemes = require('./schemes/schemes.js'); var formats = require('./formats/formats.js'); module.exports = (function () { var SUPPORTED_HASH_ALGORITHMS = { browser: ['md5', 'ripemd160', 'sha1', 'sha256', 'sha512'] }; var DEFAULT_ENCRYPTION_SCHEME = 'pkcs1'; var DEFAULT_SIGNING_SCHEME = 'pkcs1'; var EXPORT_FORMAT_ALIASES = { 'private': 'pkcs1-private-pem', 'private-der': 'pkcs1-private-der', 'public': 'pkcs8-public-pem', 'public-der': 'pkcs8-public-der', }; /** * @param key {string|buffer|object} Key in PEM format, or data for generate key {b: bits, e: exponent} * @constructor */ function NodeRSA(key, format, options) { if (!(this instanceof NodeRSA)) { return new NodeRSA(key, format, options); } if (_.isObject(format)) { options = format; format = undefined; } this.$options = { signingScheme: DEFAULT_SIGNING_SCHEME, signingSchemeOptions: { hash: 'sha256', saltLength: null }, encryptionScheme: DEFAULT_ENCRYPTION_SCHEME, encryptionSchemeOptions: { hash: 'sha1', label: null }, environment: 'browser', rsaUtils: this }; this.keyPair = new rsa.Key(); this.$cache = {}; if (Buffer.isBuffer(key) || _.isString(key)) { this.importKey(key, format); } else if (_.isObject(key)) { this.generateKeyPair(key.b, key.e); } this.setOptions(options); } /** * Set and validate options for key instance * @param options */ NodeRSA.prototype.setOptions = function (options) { options = options || {}; if (options.environment) { this.$options.environment = options.environment; } if (options.signingScheme) { if (_.isString(options.signingScheme)) { var signingScheme = options.signingScheme.toLowerCase().split('-'); if (signingScheme.length == 1) { if (SUPPORTED_HASH_ALGORITHMS.node.indexOf(signingScheme[0]) > -1) { this.$options.signingSchemeOptions = { hash: signingScheme[0] }; this.$options.signingScheme = DEFAULT_SIGNING_SCHEME; } else { this.$options.signingScheme = signingScheme[0]; this.$options.signingSchemeOptions = { hash: null }; } } else { this.$options.signingSchemeOptions = { hash: signingScheme[1] }; this.$options.signingScheme = signingScheme[0]; } } else if (_.isObject(options.signingScheme)) { this.$options.signingScheme = options.signingScheme.scheme || DEFAULT_SIGNING_SCHEME; this.$options.signingSchemeOptions = _.omit(options.signingScheme, 'scheme'); } if (!schemes.isSignature(this.$options.signingScheme)) { throw Error('Unsupported signing scheme'); } if (this.$options.signingSchemeOptions.hash && SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.signingSchemeOptions.hash) === -1) { throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment'); } } if (options.encryptionScheme) { if (_.isString(options.encryptionScheme)) { this.$options.encryptionScheme = options.encryptionScheme.toLowerCase(); this.$options.encryptionSchemeOptions = {}; } else if (_.isObject(options.encryptionScheme)) { this.$options.encryptionScheme = options.encryptionScheme.scheme || DEFAULT_ENCRYPTION_SCHEME; this.$options.encryptionSchemeOptions = _.omit(options.encryptionScheme, 'scheme'); } if (!schemes.isEncryption(this.$options.encryptionScheme)) { throw Error('Unsupported encryption scheme'); } if (this.$options.encryptionSchemeOptions.hash && SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.encryptionSchemeOptions.hash) === -1) { throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment'); } } this.keyPair.setOptions(this.$options); }; /** * Importing key * @param keyData {string|buffer|Object} * @param format {string} */ NodeRSA.prototype.importKey = function (keyData, format) { if (!keyData) { throw Error("Empty key given"); } if (format) { format = EXPORT_FORMAT_ALIASES[format] || format; } if (!formats.detectAndImport(this.keyPair, keyData, format) && format === undefined) { throw Error("Key format must be specified"); } this.$cache = {}; return this; }; /** * Check if key pair contains private key */ NodeRSA.prototype.isPrivate = function () { return this.keyPair.isPrivate(); }; /** * Check if key pair contains public key * @param [strict] {boolean} - public key only, return false if have private exponent */ NodeRSA.prototype.isPublic = function (strict) { return this.keyPair.isPublic(strict); }; /** * Check if key pair doesn't contains any data */ NodeRSA.prototype.isEmpty = function (strict) { return !(this.keyPair.n || this.keyPair.e || this.keyPair.d); }; /** * Signing data * * @param buffer {string|number|object|array|Buffer} - data for signing. Object and array will convert to JSON string. * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. * @param source_encoding {string} - optional. Encoding for given string. Default utf8. * @returns {string|Buffer} */ NodeRSA.prototype.sign = function (buffer, encoding, source_encoding) { if (!this.isPrivate()) { throw Error("This is not private key"); } var res = this.keyPair.sign(this.$getDataForEncrypt(buffer, source_encoding)); if (encoding && encoding != 'buffer') { res = res.toString(encoding); } return res; }; /** * Preparing given data for encrypting/signing. Just make new/return Buffer object. * * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string. * @param encoding {string} - optional. Encoding for given string. Default utf8. * @returns {Buffer} */ NodeRSA.prototype.$getDataForEncrypt = function (buffer, encoding) { if (_.isString(buffer) || _.isNumber(buffer)) { return Buffer.from('' + buffer, encoding || 'utf8'); } else if (Buffer.isBuffer(buffer)) { return buffer; } else if (_.isObject(buffer)) { return Buffer.from(JSON.stringify(buffer)); } else { throw Error("Unexpected data type"); } }; return NodeRSA; })(); }).call(this,require("buffer").Buffer) },{"./formats/formats.js":143,"./libs/rsa.js":147,"./schemes/schemes.js":149,"./utils":150,"buffer":178}],137:[function(require,module,exports){ // Copyright 2011 Mark Cavage All rights reserved. module.exports = { newInvalidAsn1Error: function (msg) { var e = new Error(); e.name = 'InvalidAsn1Error'; e.message = msg || ''; return e; } }; },{}],138:[function(require,module,exports){ // Copyright 2011 Mark Cavage All rights reserved. var errors = require('./errors'); var types = require('./types'); var Reader = require('./reader'); // --- Exports module.exports = { Reader: Reader, }; for (var t in types) { if (types.hasOwnProperty(t)) module.exports[t] = types[t]; } for (var e in errors) { if (errors.hasOwnProperty(e)) module.exports[e] = errors[e]; } },{"./errors":137,"./reader":139,"./types":140}],139:[function(require,module,exports){ (function (Buffer){ // Copyright 2011 Mark Cavage All rights reserved. var assert = require('assert'); // var Buffer = require('safer-buffer').Buffer; var ASN1 = require('./types'); var errors = require('./errors'); // --- Globals var newInvalidAsn1Error = errors.newInvalidAsn1Error; // --- API function Reader(data) { if (!data || !Buffer.isBuffer(data)) throw new TypeError('data must be a node Buffer'); this._buf = data; this._size = data.length; // These hold the "current" state this._len = 0; this._offset = 0; } Object.defineProperty(Reader.prototype, 'length', { enumerable: true, get: function () { return (this._len); } }); Object.defineProperty(Reader.prototype, 'offset', { enumerable: true, get: function () { return (this._offset); } }); Object.defineProperty(Reader.prototype, 'remain', { get: function () { return (this._size - this._offset); } }); Object.defineProperty(Reader.prototype, 'buffer', { get: function () { return (this._buf.slice(this._offset)); } }); /** * Reads a single byte and advances offset; you can pass in `true` to make this * a "peek" operation (i.e., get the byte, but don't advance the offset). * * @param {Boolean} peek true means don't move offset. * @return {Number} the next byte, null if not enough data. */ Reader.prototype.readByte = function (peek) { if (this._size - this._offset < 1) return null; var b = this._buf[this._offset] & 0xff; if (!peek) this._offset += 1; return b; }; Reader.prototype.peek = function () { return this.readByte(true); }; /** * Reads a (potentially) variable length off the BER buffer. This call is * not really meant to be called directly, as callers have to manipulate * the internal buffer afterwards. * * As a result of this call, you can call `Reader.length`, until the * next thing called that does a readLength. * * @return {Number} the amount of offset to advance the buffer. * @throws {InvalidAsn1Error} on bad ASN.1 */ Reader.prototype.readLength = function (offset) { if (offset === undefined) offset = this._offset; if (offset >= this._size) return null; var lenB = this._buf[offset++] & 0xff; if (lenB === null) return null; if ((lenB & 0x80) === 0x80) { lenB &= 0x7f; if (lenB === 0) throw newInvalidAsn1Error('Indefinite length not supported'); if (lenB > 4) throw newInvalidAsn1Error('encoding too long'); if (this._size - offset < lenB) return null; this._len = 0; for (var i = 0; i < lenB; i++) this._len = (this._len << 8) + (this._buf[offset++] & 0xff); } else { // Wasn't a variable length this._len = lenB; } return offset; }; /** * Parses the next sequence in this BER buffer. * * To get the length of the sequence, call `Reader.length`. * * @return {Number} the sequence's tag. */ Reader.prototype.readSequence = function (tag) { var seq = this.peek(); if (seq === null) return null; if (tag !== undefined && tag !== seq) throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + ': got 0x' + seq.toString(16)); var o = this.readLength(this._offset + 1); // stored in `length` if (o === null) return null; this._offset = o; return seq; }; Reader.prototype.readInt = function () { return this._readTag(ASN1.Integer); }; Reader.prototype.readBoolean = function () { return (this._readTag(ASN1.Boolean) === 0 ? false : true); }; Reader.prototype.readEnumeration = function () { return this._readTag(ASN1.Enumeration); }; Reader.prototype.readString = function (tag, retbuf) { if (!tag) tag = ASN1.OctetString; var b = this.peek(); if (b === null) return null; if (b !== tag) throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + ': got 0x' + b.toString(16)); var o = this.readLength(this._offset + 1); // stored in `length` if (o === null) return null; if (this.length > this._size - o) return null; this._offset = o; if (this.length === 0) return retbuf ? Buffer.alloc(0) : ''; var str = this._buf.slice(this._offset, this._offset + this.length); this._offset += this.length; return retbuf ? str : str.toString('utf8'); }; Reader.prototype.readOID = function (tag) { if (!tag) tag = ASN1.OID; var b = this.readString(tag, true); if (b === null) return null; var values = []; var value = 0; for (var i = 0; i < b.length; i++) { var byte = b[i] & 0xff; value <<= 7; value += byte & 0x7f; if ((byte & 0x80) === 0) { values.push(value); value = 0; } } value = values.shift(); values.unshift(value % 40); values.unshift((value / 40) >> 0); return values.join('.'); }; Reader.prototype._readTag = function (tag) { assert.ok(tag !== undefined); var b = this.peek(); if (b === null) return null; if (b !== tag) throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + ': got 0x' + b.toString(16)); var o = this.readLength(this._offset + 1); // stored in `length` if (o === null) return null; if (this.length > 4) throw newInvalidAsn1Error('Integer too long: ' + this.length); if (this.length > this._size - o) return null; this._offset = o; var fb = this._buf[this._offset]; var value = 0; for (var i = 0; i < this.length; i++) { value <<= 8; value |= (this._buf[this._offset++] & 0xff); } if ((fb & 0x80) === 0x80 && i !== 4) value -= (1 << (i * 8)); return value >> 0; }; // --- Exported API module.exports = Reader; }).call(this,require("buffer").Buffer) },{"./errors":137,"./types":140,"assert":173,"buffer":178}],140:[function(require,module,exports){ // Copyright 2011 Mark Cavage All rights reserved. module.exports = { EOC: 0, Boolean: 1, Integer: 2, BitString: 3, OctetString: 4, Null: 5, OID: 6, ObjectDescriptor: 7, External: 8, Real: 9, // float Enumeration: 10, PDV: 11, Utf8String: 12, RelativeOID: 13, Sequence: 16, Set: 17, NumericString: 18, PrintableString: 19, T61String: 20, VideotexString: 21, IA5String: 22, UTCTime: 23, GeneralizedTime: 24, GraphicString: 25, VisibleString: 26, GeneralString: 28, UniversalString: 29, CharacterString: 30, BMPString: 31, Constructor: 32, Context: 128 }; },{}],141:[function(require,module,exports){ // Copyright 2011 Mark Cavage All rights reserved. // If you have no idea what ASN.1 or BER is, see this: // ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc var Ber = require('./ber/index'); // --- Exported API module.exports = { Ber: Ber, BerReader: Ber.Reader, }; },{"./ber/index":138}],142:[function(require,module,exports){ var _ = require('../utils')._; var utils = require('../utils'); module.exports = { privateExport: function (key, options) { return { n: key.n.toBuffer(), e: key.e, d: key.d.toBuffer(), p: key.p.toBuffer(), q: key.q.toBuffer(), dmp1: key.dmp1.toBuffer(), dmq1: key.dmq1.toBuffer(), coeff: key.coeff.toBuffer() }; }, privateImport: function (key, data, options) { if (data.n && data.e && data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) { key.setPrivate( data.n, data.e, data.d, data.p, data.q, data.dmp1, data.dmq1, data.coeff ); } else { throw Error("Invalid key data"); } }, publicExport: function (key, options) { return { n: key.n.toBuffer(), e: key.e }; }, publicImport: function (key, data, options) { if (data.n && data.e) { key.setPublic( data.n, data.e ); } else { throw Error("Invalid key data"); } }, /** * Trying autodetect and import key * @param key * @param data */ autoImport: function (key, data) { if (data.n && data.e) { if (data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) { module.exports.privateImport(key, data); return true; } else { module.exports.publicImport(key, data); return true; } } return false; } }; },{"../utils":150}],143:[function(require,module,exports){ var _ = require('../utils')._; module.exports = { pkcs1: require('./pkcs1'), pkcs8: require('./pkcs8'), components: require('./components'), detectAndImport: function (key, data, format) { if (format === undefined) { for (var scheme in module.exports) { if (typeof module.exports[scheme].autoImport === 'function' && module.exports[scheme].autoImport(key, data)) { return true; } } } else if (format) { var fmt = formatParse(format); if (module.exports[fmt.scheme]) { if (fmt.keyType === 'private') { module.exports[fmt.scheme].privateImport(key, data, fmt.keyOpt); } else { module.exports[fmt.scheme].publicImport(key, data, fmt.keyOpt); } } else { throw Error('Unsupported key format'); } } return false; }, }; },{"../utils":150,"./components":142,"./pkcs1":144,"./pkcs8":145}],144:[function(require,module,exports){ (function (Buffer){ var ber = require('../asn1/index').Ber; var _ = require('../utils')._; var utils = require('../utils'); const PRIVATE_OPENING_BOUNDARY = '-----BEGIN RSA PRIVATE KEY-----'; const PRIVATE_CLOSING_BOUNDARY = '-----END RSA PRIVATE KEY-----'; const PUBLIC_OPENING_BOUNDARY = '-----BEGIN RSA PUBLIC KEY-----'; const PUBLIC_CLOSING_BOUNDARY = '-----END RSA PUBLIC KEY-----'; module.exports = { privateExport: function (key, options) { options = options || {}; var n = key.n.toBuffer(); var d = key.d.toBuffer(); var p = key.p.toBuffer(); var q = key.q.toBuffer(); var dmp1 = key.dmp1.toBuffer(); var dmq1 = key.dmq1.toBuffer(); var coeff = key.coeff.toBuffer(); var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic var writer = new ber.Writer({size: length}); writer.startSequence(); writer.writeInt(0); writer.writeBuffer(n, 2); writer.writeInt(key.e); writer.writeBuffer(d, 2); writer.writeBuffer(p, 2); writer.writeBuffer(q, 2); writer.writeBuffer(dmp1, 2); writer.writeBuffer(dmq1, 2); writer.writeBuffer(coeff, 2); writer.endSequence(); if (options.type === 'der') { return writer.buffer; } else { return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; } }, privateImport: function (key, data, options) { options = options || {}; var buffer; if (options.type !== 'der') { if (Buffer.isBuffer(data)) { data = data.toString('utf8'); } if (_.isString(data)) { var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) .replace(/\s+|\n\r|\n|\r$/gm, ''); buffer = Buffer.from(pem, 'base64'); } else { throw Error('Unsupported key format'); } } else if (Buffer.isBuffer(data)) { buffer = data; } else { throw Error('Unsupported key format'); } var reader = new ber.Reader(buffer); reader.readSequence(); reader.readString(2, true); // just zero key.setPrivate( reader.readString(2, true), // modulus reader.readString(2, true), // publicExponent reader.readString(2, true), // privateExponent reader.readString(2, true), // prime1 reader.readString(2, true), // prime2 reader.readString(2, true), // exponent1 -- d mod (p1) reader.readString(2, true), // exponent2 -- d mod (q-1) reader.readString(2, true) // coefficient -- (inverse of q) mod p ); }, publicExport: function (key, options) { options = options || {}; var n = key.n.toBuffer(); var length = n.length + 512; // magic var bodyWriter = new ber.Writer({size: length}); bodyWriter.startSequence(); bodyWriter.writeBuffer(n, 2); bodyWriter.writeInt(key.e); bodyWriter.endSequence(); if (options.type === 'der') { return bodyWriter.buffer; } else { return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(bodyWriter.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; } }, publicImport: function (key, data, options) { options = options || {}; var buffer; if (options.type !== 'der') { if (Buffer.isBuffer(data)) { data = data.toString('utf8'); } if (_.isString(data)) { var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) .replace(/\s+|\n\r|\n|\r$/gm, ''); buffer = Buffer.from(pem, 'base64'); } } else if (Buffer.isBuffer(data)) { buffer = data; } else { throw Error('Unsupported key format'); } var body = new ber.Reader(buffer); body.readSequence(); key.setPublic( body.readString(0x02, true), // modulus body.readString(0x02, true) // publicExponent ); }, /** * Trying autodetect and import key * @param key * @param data */ autoImport: function (key, data) { // [\S\s]* matches zero or more of any character if (/^[\S\s]*-----BEGIN RSA PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PRIVATE KEY-----[\S\s]*$/g.test(data)) { module.exports.privateImport(key, data); return true; } if (/^[\S\s]*-----BEGIN RSA PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PUBLIC KEY-----[\S\s]*$/g.test(data)) { module.exports.publicImport(key, data); return true; } return false; } }; }).call(this,require("buffer").Buffer) },{"../asn1/index":141,"../utils":150,"buffer":178}],145:[function(require,module,exports){ (function (Buffer){ var ber = require('../asn1/index').Ber; var _ = require('../utils')._; var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1'; var utils = require('../utils'); const PRIVATE_OPENING_BOUNDARY = '-----BEGIN PRIVATE KEY-----'; const PRIVATE_CLOSING_BOUNDARY = '-----END PRIVATE KEY-----'; const PUBLIC_OPENING_BOUNDARY = '-----BEGIN PUBLIC KEY-----'; const PUBLIC_CLOSING_BOUNDARY = '-----END PUBLIC KEY-----'; module.exports = { privateExport: function (key, options) { options = options || {}; var n = key.n.toBuffer(); var d = key.d.toBuffer(); var p = key.p.toBuffer(); var q = key.q.toBuffer(); var dmp1 = key.dmp1.toBuffer(); var dmq1 = key.dmq1.toBuffer(); var coeff = key.coeff.toBuffer(); var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic var bodyWriter = new ber.Writer({size: length}); bodyWriter.startSequence(); bodyWriter.writeInt(0); bodyWriter.writeBuffer(n, 2); bodyWriter.writeInt(key.e); bodyWriter.writeBuffer(d, 2); bodyWriter.writeBuffer(p, 2); bodyWriter.writeBuffer(q, 2); bodyWriter.writeBuffer(dmp1, 2); bodyWriter.writeBuffer(dmq1, 2); bodyWriter.writeBuffer(coeff, 2); bodyWriter.endSequence(); var writer = new ber.Writer({size: length}); writer.startSequence(); writer.writeInt(0); writer.startSequence(); writer.writeOID(PUBLIC_RSA_OID); writer.writeNull(); writer.endSequence(); writer.writeBuffer(bodyWriter.buffer, 4); writer.endSequence(); if (options.type === 'der') { return writer.buffer; } else { return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; } }, privateImport: function (key, data, options) { options = options || {}; var buffer; if (options.type !== 'der') { if (Buffer.isBuffer(data)) { data = data.toString('utf8'); } if (_.isString(data)) { var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) .replace('-----END PRIVATE KEY-----', '') .replace(/\s+|\n\r|\n|\r$/gm, ''); buffer = Buffer.from(pem, 'base64'); } else { throw Error('Unsupported key format'); } } else if (Buffer.isBuffer(data)) { buffer = data; } else { throw Error('Unsupported key format'); } var reader = new ber.Reader(buffer); reader.readSequence(); reader.readInt(0); var header = new ber.Reader(reader.readString(0x30, true)); if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) { throw Error('Invalid Public key format'); } var body = new ber.Reader(reader.readString(0x04, true)); body.readSequence(); body.readString(2, true); // just zero key.setPrivate( body.readString(2, true), // modulus body.readString(2, true), // publicExponent body.readString(2, true), // privateExponent body.readString(2, true), // prime1 body.readString(2, true), // prime2 body.readString(2, true), // exponent1 -- d mod (p1) body.readString(2, true), // exponent2 -- d mod (q-1) body.readString(2, true) // coefficient -- (inverse of q) mod p ); }, publicExport: function (key, options) { options = options || {}; var n = key.n.toBuffer(); var length = n.length + 512; // magic var bodyWriter = new ber.Writer({size: length}); bodyWriter.writeByte(0); bodyWriter.startSequence(); bodyWriter.writeBuffer(n, 2); bodyWriter.writeInt(key.e); bodyWriter.endSequence(); var writer = new ber.Writer({size: length}); writer.startSequence(); writer.startSequence(); writer.writeOID(PUBLIC_RSA_OID); writer.writeNull(); writer.endSequence(); writer.writeBuffer(bodyWriter.buffer, 3); writer.endSequence(); if (options.type === 'der') { return writer.buffer; } else { return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; } }, publicImport: function (key, data, options) { options = options || {}; var buffer; if (options.type !== 'der') { if (Buffer.isBuffer(data)) { data = data.toString('utf8'); } if (_.isString(data)) { var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) .replace(/\s+|\n\r|\n|\r$/gm, ''); buffer = Buffer.from(pem, 'base64'); } } else if (Buffer.isBuffer(data)) { buffer = data; } else { throw Error('Unsupported key format'); } var reader = new ber.Reader(buffer); reader.readSequence(); var header = new ber.Reader(reader.readString(0x30, true)); if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) { throw Error('Invalid Public key format'); } var body = new ber.Reader(reader.readString(0x03, true)); body.readByte(); body.readSequence(); key.setPublic( body.readString(0x02, true), // modulus body.readString(0x02, true) // publicExponent ); }, /** * Trying autodetect and import key * @param key * @param data */ autoImport: function (key, data) { if (/^[\S\s]*-----BEGIN PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PRIVATE KEY-----[\S\s]*$/g.test(data)) { module.exports.privateImport(key, data); return true; } if (/^[\S\s]*-----BEGIN PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PUBLIC KEY-----[\S\s]*$/g.test(data)) { module.exports.publicImport(key, data); return true; } return false; } }; }).call(this,require("buffer").Buffer) },{"../asn1/index":141,"../utils":150,"buffer":178}],146:[function(require,module,exports){ (function (Buffer){ /* * Basic JavaScript BN library - subset useful for RSA encryption. * * Copyright (c) 2003-2005 Tom Wu * ~BSD license~ * * Added Node.js Buffers support * 2014 rzcoder * * Deleted some stuff to save bytes * frosty00 */ var _ = require('../utils')._; // Bits per digit var dbits; // (public) Constructor function BigInteger(a, b) { if (a != null) { if ("number" == typeof a) { this.fromNumber(a, b); } else if (Buffer.isBuffer(a)) { this.fromBuffer(a); } else if (b == null && "string" != typeof a) { this.fromByteArray(a); } else { this.fromString(a, b); } } } // return new, unset BigInteger function nbi() { return new BigInteger(null); } // am: Compute w_j += (x*this_i), propagate carries, // c is initial carry, returns final carry. // c < 3*dvalue, x < 2*dvalue, this_i < dvalue // We need to select the fastest one that works in this environment. // Alternately, set max digit bits to 28 since some // browsers slow down when dealing with 32-bit numbers. function am3(i, x, w, j, c, n) { var xl = x & 0x3fff, xh = x >> 14; while (--n >= 0) { var l = this[i] & 0x3fff; var h = this[i++] >> 14; var m = xh * l + h * xl; l = xl * l + ((m & 0x3fff) << 14) + w[j] + c; c = (l >> 28) + (m >> 14) + xh * h; w[j++] = l & 0xfffffff; } return c; } // We need to select the fastest one that works in this environment. //if (j_lm && (navigator.appName == "Microsoft Internet Explorer")) { // BigInteger.prototype.am = am2; // dbits = 30; //} else if (j_lm && (navigator.appName != "Netscape")) { // BigInteger.prototype.am = am1; // dbits = 26; //} else { // Mozilla/Netscape seems to prefer am3 // BigInteger.prototype.am = am3; // dbits = 28; //} // For node.js, we pick am3 with max dbits to 28. BigInteger.prototype.am = am3; dbits = 28; BigInteger.prototype.DB = dbits; BigInteger.prototype.DM = ((1 << dbits) - 1); BigInteger.prototype.DV = (1 << dbits); var BI_FP = 52; BigInteger.prototype.FV = Math.pow(2, BI_FP); BigInteger.prototype.F1 = BI_FP - dbits; BigInteger.prototype.F2 = 2 * dbits - BI_FP; // Digit conversions var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; var BI_RC = new Array(); var rr, vv; rr = "0".charCodeAt(0); for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; rr = "a".charCodeAt(0); for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; rr = "A".charCodeAt(0); for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; function int2char(n) { return BI_RM.charAt(n); } function intAt(s, i) { var c = BI_RC[s.charCodeAt(i)]; return (c == null) ? -1 : c; } // (protected) copy this to r function bnpCopyTo(r) { for (var i = this.t - 1; i >= 0; --i) r[i] = this[i]; r.t = this.t; r.s = this.s; } // (protected) set from integer value x, -DV <= x < DV function bnpFromInt(x) { this.t = 1; this.s = (x < 0) ? -1 : 0; if (x > 0) this[0] = x; else if (x < -1) this[0] = x + DV; else this.t = 0; } // return bigint initialized to value function nbv(i) { var r = nbi(); r.fromInt(i); return r; } // (protected) set from string and radix function bnpFromString(data, radix, unsigned) { var k; switch (radix) { case 2: k = 1; break; case 4: k = 2; break; case 8: k = 3; break; case 16: k = 4; break; case 32: k = 5; break; case 256: k = 8; break; default: this.fromRadix(data, radix); return; } this.t = 0; this.s = 0; var i = data.length; var mi = false; var sh = 0; while (--i >= 0) { var x = (k == 8) ? data[i] & 0xff : intAt(data, i); if (x < 0) { if (data.charAt(i) == "-") mi = true; continue; } mi = false; if (sh === 0) this[this.t++] = x; else if (sh + k > this.DB) { this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh; this[this.t++] = (x >> (this.DB - sh)); } else this[this.t - 1] |= x << sh; sh += k; if (sh >= this.DB) sh -= this.DB; } if ((!unsigned) && k == 8 && (data[0] & 0x80) != 0) { this.s = -1; if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; } this.clamp(); if (mi) BigInteger.ZERO.subTo(this, this); } function bnpFromByteArray(a, unsigned) { this.fromString(a, 256, unsigned) } function bnpFromBuffer(a) { this.fromString(a, 256, true) } // (protected) clamp off excess high words function bnpClamp() { var c = this.s & this.DM; while (this.t > 0 && this[this.t - 1] == c) --this.t; } // (public) return string representation in given radix function bnToString(b) { if (this.s < 0) return "-" + this.negate().toString(b); var k; if (b == 16) k = 4; else if (b == 8) k = 3; else if (b == 2) k = 1; else if (b == 32) k = 5; else if (b == 4) k = 2; else return this.toRadix(b); var km = (1 << k) - 1, d, m = false, r = "", i = this.t; var p = this.DB - (i * this.DB) % k; if (i-- > 0) { if (p < this.DB && (d = this[i] >> p) > 0) { m = true; r = int2char(d); } while (i >= 0) { if (p < k) { d = (this[i] & ((1 << p) - 1)) << (k - p); d |= this[--i] >> (p += this.DB - k); } else { d = (this[i] >> (p -= k)) & km; if (p <= 0) { p += this.DB; --i; } } if (d > 0) m = true; if (m) r += int2char(d); } } return m ? r : "0"; } // (public) -this function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this, r); return r; } // (public) |this| function bnAbs() { return (this.s < 0) ? this.negate() : this; } // (public) return + if this > a, - if this < a, 0 if equal function bnCompareTo(a) { var r = this.s - a.s; if (r != 0) return r; var i = this.t; r = i - a.t; if (r != 0) return (this.s < 0) ? -r : r; while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r; return 0; } // returns bit length of the integer x function nbits(x) { var r = 1, t; if ((t = x >>> 16) != 0) { x = t; r += 16; } if ((t = x >> 8) != 0) { x = t; r += 8; } if ((t = x >> 4) != 0) { x = t; r += 4; } if ((t = x >> 2) != 0) { x = t; r += 2; } if ((t = x >> 1) != 0) { x = t; r += 1; } return r; } // (public) return the number of bits in "this" function bnBitLength() { if (this.t <= 0) return 0; return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM)); } // (protected) r = this << n*DB function bnpDLShiftTo(n, r) { var i; for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i]; for (i = n - 1; i >= 0; --i) r[i] = 0; r.t = this.t + n; r.s = this.s; } // (protected) r = this >> n*DB function bnpDRShiftTo(n, r) { for (var i = n; i < this.t; ++i) r[i - n] = this[i]; r.t = Math.max(this.t - n, 0); r.s = this.s; } // (protected) r = this << n function bnpLShiftTo(n, r) { var bs = n % this.DB; var cbs = this.DB - bs; var bm = (1 << cbs) - 1; var ds = Math.floor(n / this.DB), c = (this.s << bs) & this.DM, i; for (i = this.t - 1; i >= 0; --i) { r[i + ds + 1] = (this[i] >> cbs) | c; c = (this[i] & bm) << bs; } for (i = ds - 1; i >= 0; --i) r[i] = 0; r[ds] = c; r.t = this.t + ds + 1; r.s = this.s; r.clamp(); } // (protected) r = this >> n function bnpRShiftTo(n, r) { r.s = this.s; var ds = Math.floor(n / this.DB); if (ds >= this.t) { r.t = 0; return; } var bs = n % this.DB; var cbs = this.DB - bs; var bm = (1 << bs) - 1; r[0] = this[ds] >> bs; for (var i = ds + 1; i < this.t; ++i) { r[i - ds - 1] |= (this[i] & bm) << cbs; r[i - ds] = this[i] >> bs; } if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs; r.t = this.t - ds; r.clamp(); } // (protected) r = this - a function bnpSubTo(a, r) { var i = 0, c = 0, m = Math.min(a.t, this.t); while (i < m) { c += this[i] - a[i]; r[i++] = c & this.DM; c >>= this.DB; } if (a.t < this.t) { c -= a.s; while (i < this.t) { c += this[i]; r[i++] = c & this.DM; c >>= this.DB; } c += this.s; } else { c += this.s; while (i < a.t) { c -= a[i]; r[i++] = c & this.DM; c >>= this.DB; } c -= a.s; } r.s = (c < 0) ? -1 : 0; if (c < -1) r[i++] = this.DV + c; else if (c > 0) r[i++] = c; r.t = i; r.clamp(); } // (protected) r = this * a, r != this,a (HAC 14.12) // "this" should be the larger one if appropriate. function bnpMultiplyTo(a, r) { var x = this.abs(), y = a.abs(); var i = x.t; r.t = i + y.t; while (--i >= 0) r[i] = 0; for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t); r.s = 0; r.clamp(); if (this.s != a.s) BigInteger.ZERO.subTo(r, r); } // (protected) r = this^2, r != this (HAC 14.16) function bnpSquareTo(r) { var x = this.abs(); var i = r.t = 2 * x.t; while (--i >= 0) r[i] = 0; for (i = 0; i < x.t - 1; ++i) { var c = x.am(i, x[i], r, 2 * i, 0, 1); if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) { r[i + x.t] -= x.DV; r[i + x.t + 1] = 1; } } if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1); r.s = 0; r.clamp(); } // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) // r != q, this != m. q or r may be null. function bnpDivRemTo(m, q, r) { var pm = m.abs(); if (pm.t <= 0) return; var pt = this.abs(); if (pt.t < pm.t) { if (q != null) q.fromInt(0); if (r != null) this.copyTo(r); return; } if (r == null) r = nbi(); var y = nbi(), ts = this.s, ms = m.s; var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus if (nsh > 0) { pm.lShiftTo(nsh, y); pt.lShiftTo(nsh, r); } else { pm.copyTo(y); pt.copyTo(r); } var ys = y.t; var y0 = y[ys - 1]; if (y0 === 0) return; var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0); var d1 = this.FV / yt, d2 = (1 << this.F1) / yt, e = 1 << this.F2; var i = r.t, j = i - ys, t = (q == null) ? nbi() : q; y.dlShiftTo(j, t); if (r.compareTo(t) >= 0) { r[r.t++] = 1; r.subTo(t, r); } BigInteger.ONE.dlShiftTo(ys, t); t.subTo(y, y); // "negative" y so we can replace sub with am later while (y.t < ys) y[y.t++] = 0; while (--j >= 0) { // Estimate quotient digit var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out y.dlShiftTo(j, t); r.subTo(t, r); while (r[i] < --qd) r.subTo(t, r); } } if (q != null) { r.drShiftTo(ys, q); if (ts != ms) BigInteger.ZERO.subTo(q, q); } r.t = ys; r.clamp(); if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder if (ts < 0) BigInteger.ZERO.subTo(r, r); } // (public) this mod a function bnMod(a) { var r = nbi(); this.abs().divRemTo(a, null, r); if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r); return r; } // Modular reduction using "classic" algorithm function Classic(m) { this.m = m; } function cConvert(x) { if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); else return x; } function cRevert(x) { return x; } function cReduce(x) { x.divRemTo(this.m, null, x); } function cMulTo(x, y, r) { x.multiplyTo(y, r); this.reduce(r); } function cSqrTo(x, r) { x.squareTo(r); this.reduce(r); } Classic.prototype.convert = cConvert; Classic.prototype.revert = cRevert; Classic.prototype.reduce = cReduce; Classic.prototype.mulTo = cMulTo; Classic.prototype.sqrTo = cSqrTo; // (protected) return "-1/this % 2^DB"; useful for Mont. reduction // justification: // xy == 1 (mod m) // xy = 1+km // xy(2-xy) = (1+km)(1-km) // x[y(2-xy)] = 1-k^2m^2 // x[y(2-xy)] == 1 (mod m^2) // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. // JS multiply "overflows" differently from C/C++, so care is needed here. function bnpInvDigit() { if (this.t < 1) return 0; var x = this[0]; if ((x & 1) === 0) return 0; var y = x & 3; // y == 1/x mod 2^2 y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 // last step - calculate inverse mod DV directly; // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits // we really want the negative inverse, and -DV < y < DV return (y > 0) ? this.DV - y : -y; } // Montgomery reduction function Montgomery(m) { this.m = m; this.mp = m.invDigit(); this.mpl = this.mp & 0x7fff; this.mph = this.mp >> 15; this.um = (1 << (m.DB - 15)) - 1; this.mt2 = 2 * m.t; } // xR mod m function montConvert(x) { var r = nbi(); x.abs().dlShiftTo(this.m.t, r); r.divRemTo(this.m, null, r); if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r); return r; } // x/R mod m function montRevert(x) { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } // x = x/R mod m (HAC 14.32) function montReduce(x) { while (x.t <= this.mt2) // pad x so am has enough room later x[x.t++] = 0; for (var i = 0; i < this.m.t; ++i) { // faster way of calculating u0 = x[i]*mp mod DV var j = x[i] & 0x7fff; var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM; // use am to combine the multiply-shift-add into one call j = i + this.m.t; x[j] += this.m.am(0, u0, x, i, 0, this.m.t); // propagate carry while (x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } } x.clamp(); x.drShiftTo(this.m.t, x); if (x.compareTo(this.m) >= 0) x.subTo(this.m, x); } // r = "x^2/R mod m"; x != r function montSqrTo(x, r) { x.squareTo(r); this.reduce(r); } // r = "xy/R mod m"; x,y != r function montMulTo(x, y, r) { x.multiplyTo(y, r); this.reduce(r); } Montgomery.prototype.convert = montConvert; Montgomery.prototype.revert = montRevert; Montgomery.prototype.reduce = montReduce; Montgomery.prototype.mulTo = montMulTo; Montgomery.prototype.sqrTo = montSqrTo; // (protected) true iff this is even function bnpIsEven() { return ((this.t > 0) ? (this[0] & 1) : this.s) === 0; } // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) function bnpExp(e, z) { if (e > 0xffffffff || e < 1) return BigInteger.ONE; var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e) - 1; g.copyTo(r); while (--i >= 0) { z.sqrTo(r, r2); if ((e & (1 << i)) > 0) z.mulTo(r2, g, r); else { var t = r; r = r2; r2 = t; } } return z.revert(r); } // Copyright (c) 2005-2009 Tom Wu // All Rights Reserved. // See "LICENSE" for details. // Extended JavaScript BN functions, required for RSA private ops. // Version 1.1: new BigInteger("0", 10) returns "proper" zero // Version 1.2: square() API, isProbablePrime fix //(public) function bnClone() { var r = nbi(); this.copyTo(r); return r; } //(public) return value as integer function bnIntValue() { if (this.s < 0) { if (this.t == 1) return this[0] - this.DV; else if (this.t === 0) return -1; } else if (this.t == 1) return this[0]; else if (this.t === 0) return 0; // assumes 16 < DB < 32 return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0]; } //(protected) return x s.t. r^x < DV function bnpChunkSize(r) { return Math.floor(Math.LN2 * this.DB / Math.log(r)); } //(public) 0 if this === 0, 1 if this > 0 function bnSigNum() { if (this.s < 0) return -1; else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; else return 1; } //(protected) convert to radix string function bnpToRadix(b) { if (b == null) b = 10; if (this.signum() === 0 || b < 2 || b > 36) return "0"; var cs = this.chunkSize(b); var a = Math.pow(b, cs); var d = nbv(a), y = nbi(), z = nbi(), r = ""; this.divRemTo(d, y, z); while (y.signum() > 0) { r = (a + z.intValue()).toString(b).substr(1) + r; y.divRemTo(d, y, z); } return z.intValue().toString(b) + r; } //(protected) convert from radix string function bnpFromRadix(s, b) { this.fromInt(0); if (b == null) b = 10; var cs = this.chunkSize(b); var d = Math.pow(b, cs), mi = false, j = 0, w = 0; for (var i = 0; i < s.length; ++i) { var x = intAt(s, i); if (x < 0) { if (s.charAt(i) == "-" && this.signum() === 0) mi = true; continue; } w = b * w + x; if (++j >= cs) { this.dMultiply(d); this.dAddOffset(w, 0); j = 0; w = 0; } } if (j > 0) { this.dMultiply(Math.pow(b, j)); this.dAddOffset(w, 0); } if (mi) BigInteger.ZERO.subTo(this, this); } //(public) convert to bigendian byte array function bnToByteArray() { var i = this.t, r = new Array(); r[0] = this.s; var p = this.DB - (i * this.DB) % 8, d, k = 0; if (i-- > 0) { if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) r[k++] = d | (this.s << (this.DB - p)); while (i >= 0) { if (p < 8) { d = (this[i] & ((1 << p) - 1)) << (8 - p); d |= this[--i] >> (p += this.DB - 8); } else { d = (this[i] >> (p -= 8)) & 0xff; if (p <= 0) { p += this.DB; --i; } } if ((d & 0x80) != 0) d |= -256; if (k === 0 && (this.s & 0x80) != (d & 0x80)) ++k; if (k > 0 || d != this.s) r[k++] = d; } } return r; } /** * return Buffer object * @param trim {boolean} slice buffer if first element == 0 * @returns {Buffer} */ function bnToBuffer(trimOrSize) { var res = Buffer.from(this.toByteArray()); if (trimOrSize === true && res[0] === 0) { res = res.slice(1); } else if (_.isNumber(trimOrSize)) { if (res.length > trimOrSize) { for (var i = 0; i < res.length - trimOrSize; i++) { if (res[i] !== 0) { return null; } } return res.slice(res.length - trimOrSize); } else if (res.length < trimOrSize) { var padded = Buffer.alloc(trimOrSize); padded.fill(0, 0, trimOrSize - res.length); res.copy(padded, trimOrSize - res.length); return padded; } } return res; } function bnEquals(a) { return (this.compareTo(a) == 0); } function bnMin(a) { return (this.compareTo(a) < 0) ? this : a; } function bnMax(a) { return (this.compareTo(a) > 0) ? this : a; } //(protected) r = this op a (bitwise) function bnpBitwiseTo(a, op, r) { var i, f, m = Math.min(a.t, this.t); for (i = 0; i < m; ++i) r[i] = op(this[i], a[i]); if (a.t < this.t) { f = a.s & this.DM; for (i = m; i < this.t; ++i) r[i] = op(this[i], f); r.t = this.t; } else { f = this.s & this.DM; for (i = m; i < a.t; ++i) r[i] = op(f, a[i]); r.t = a.t; } r.s = op(this.s, a.s); r.clamp(); } //(public) this & a function op_and(x, y) { return x & y; } function bnAnd(a) { var r = nbi(); this.bitwiseTo(a, op_and, r); return r; } //(public) this | a function op_or(x, y) { return x | y; } function bnOr(a) { var r = nbi(); this.bitwiseTo(a, op_or, r); return r; } //(public) this ^ a function op_xor(x, y) { return x ^ y; } function bnXor(a) { var r = nbi(); this.bitwiseTo(a, op_xor, r); return r; } //(public) this & ~a function op_andnot(x, y) { return x & ~y; } //(public) ~this function bnNot() { var r = nbi(); for (var i = 0; i < this.t; ++i) r[i] = this.DM & ~this[i]; r.t = this.t; r.s = ~this.s; return r; } //return index of lowest 1-bit in x, x < 2^31 function lbit(x) { if (x === 0) return -1; var r = 0; if ((x & 0xffff) === 0) { x >>= 16; r += 16; } if ((x & 0xff) === 0) { x >>= 8; r += 8; } if ((x & 0xf) === 0) { x >>= 4; r += 4; } if ((x & 3) === 0) { x >>= 2; r += 2; } if ((x & 1) === 0) ++r; return r; } //(public) returns index of lowest 1-bit (or -1 if none) function bnGetLowestSetBit() { for (var i = 0; i < this.t; ++i) if (this[i] != 0) return i * this.DB + lbit(this[i]); if (this.s < 0) return this.t * this.DB; return -1; } //return number of 1 bits in x function cbit(x) { var r = 0; while (x != 0) { x &= x - 1; ++r; } return r; } //(protected) r = this + a function bnpAddTo(a, r) { var i = 0, c = 0, m = Math.min(a.t, this.t); while (i < m) { c += this[i] + a[i]; r[i++] = c & this.DM; c >>= this.DB; } if (a.t < this.t) { c += a.s; while (i < this.t) { c += this[i]; r[i++] = c & this.DM; c >>= this.DB; } c += this.s; } else { c += this.s; while (i < a.t) { c += a[i]; r[i++] = c & this.DM; c >>= this.DB; } c += a.s; } r.s = (c < 0) ? -1 : 0; if (c > 0) r[i++] = c; else if (c < -1) r[i++] = this.DV + c; r.t = i; r.clamp(); } //(public) this + a function bnAdd(a) { var r = nbi(); this.addTo(a, r); return r; } //(public) this - a function bnSubtract(a) { var r = nbi(); this.subTo(a, r); return r; } //(public) this * a function bnMultiply(a) { var r = nbi(); this.multiplyTo(a, r); return r; } // (public) this^2 function bnSquare() { var r = nbi(); this.squareTo(r); return r; } //(public) this / a function bnDivide(a) { var r = nbi(); this.divRemTo(a, r, null); return r; } //(public) this % a function bnRemainder(a) { var r = nbi(); this.divRemTo(a, null, r); return r; } //(protected) this *= n, this >= 0, 1 < n < DV function bnpDMultiply(n) { this[this.t] = this.am(0, n - 1, this, 0, 0, this.t); ++this.t; this.clamp(); } //(protected) this += n << w words, this >= 0 function bnpDAddOffset(n, w) { if (n === 0) return; while (this.t <= w) this[this.t++] = 0; this[w] += n; while (this[w] >= this.DV) { this[w] -= this.DV; if (++w >= this.t) this[this.t++] = 0; ++this[w]; } } //A "null" reducer function NullExp() { } function nNop(x) { return x; } function nMulTo(x, y, r) { x.multiplyTo(y, r); } function nSqrTo(x, r) { x.squareTo(r); } NullExp.prototype.convert = nNop; NullExp.prototype.revert = nNop; NullExp.prototype.mulTo = nMulTo; NullExp.prototype.sqrTo = nSqrTo; //(public) this^e function bnPow(e) { return this.exp(e, new NullExp()); } //(protected) r = lower n words of "this * a", a.t <= n //"this" should be the larger one if appropriate. function bnpMultiplyLowerTo(a, n, r) { var i = Math.min(this.t + a.t, n); r.s = 0; // assumes a,this >= 0 r.t = i; while (i > 0) r[--i] = 0; var j; for (j = r.t - this.t; i < j; ++i) r[i + this.t] = this.am(0, a[i], r, i, 0, this.t); for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a[i], r, i, 0, n - i); r.clamp(); } //(protected) r = "this * a" without lower n words, n > 0 //"this" should be the larger one if appropriate. function bnpMultiplyUpperTo(a, n, r) { --n; var i = r.t = this.t + a.t - n; r.s = 0; // assumes a,this >= 0 while (--i >= 0) r[i] = 0; for (i = Math.max(n - this.t, 0); i < a.t; ++i) r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n); r.clamp(); r.drShiftTo(1, r); } //Barrett modular reduction function Barrett(m) { // setup Barrett this.r2 = nbi(); this.q3 = nbi(); BigInteger.ONE.dlShiftTo(2 * m.t, this.r2); this.mu = this.r2.divide(m); this.m = m; } function barrettConvert(x) { if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m); else if (x.compareTo(this.m) < 0) return x; else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } } function barrettRevert(x) { return x; } //x = x mod m (HAC 14.42) function barrettReduce(x) { x.drShiftTo(this.m.t - 1, this.r2); if (x.t > this.m.t + 1) { x.t = this.m.t + 1; x.clamp(); } this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1); x.subTo(this.r2, x); while (x.compareTo(this.m) >= 0) x.subTo(this.m, x); } //r = x^2 mod m; x != r function barrettSqrTo(x, r) { x.squareTo(r); this.reduce(r); } //r = x*y mod m; x,y != r function barrettMulTo(x, y, r) { x.multiplyTo(y, r); this.reduce(r); } Barrett.prototype.convert = barrettConvert; Barrett.prototype.revert = barrettRevert; Barrett.prototype.reduce = barrettReduce; Barrett.prototype.mulTo = barrettMulTo; Barrett.prototype.sqrTo = barrettSqrTo; //(public) this^e % m (HAC 14.85) function bnModPow(e, m) { var i = e.bitLength(), k, r = nbv(1), z; if (i <= 0) return r; else if (i < 18) k = 1; else if (i < 48) k = 3; else if (i < 144) k = 4; else if (i < 768) k = 5; else k = 6; if (i < 8) z = new Classic(m); else if (m.isEven()) z = new Barrett(m); else z = new Montgomery(m); // precomputation var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1; g[1] = z.convert(this); if (k > 1) { var g2 = nbi(); z.sqrTo(g[1], g2); while (n <= km) { g[n] = nbi(); z.mulTo(g2, g[n - 2], g[n]); n += 2; } } var j = e.t - 1, w, is1 = true, r2 = nbi(), t; i = nbits(e[j]) - 1; while (j >= 0) { if (i >= k1) w = (e[j] >> (i - k1)) & km; else { w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i); if (j > 0) w |= e[j - 1] >> (this.DB + i - k1); } n = k; while ((w & 1) === 0) { w >>= 1; --n; } if ((i -= n) < 0) { i += this.DB; --j; } if (is1) { // ret == 1, don't bother squaring or multiplying it g[w].copyTo(r); is1 = false; } else { while (n > 1) { z.sqrTo(r, r2); z.sqrTo(r2, r); n -= 2; } if (n > 0) z.sqrTo(r, r2); else { t = r; r = r2; r2 = t; } z.mulTo(r2, g[w], r); } while (j >= 0 && (e[j] & (1 << i)) === 0) { z.sqrTo(r, r2); t = r; r = r2; r2 = t; if (--i < 0) { i = this.DB - 1; --j; } } } return z.revert(r); } //(public) gcd(this,a) (HAC 14.54) function bnGCD(a) { var x = (this.s < 0) ? this.negate() : this.clone(); var y = (a.s < 0) ? a.negate() : a.clone(); if (x.compareTo(y) < 0) { var t = x; x = y; y = t; } var i = x.getLowestSetBit(), g = y.getLowestSetBit(); if (g < 0) return x; if (i < g) g = i; if (g > 0) { x.rShiftTo(g, x); y.rShiftTo(g, y); } while (x.signum() > 0) { if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x); if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y); if (x.compareTo(y) >= 0) { x.subTo(y, x); x.rShiftTo(1, x); } else { y.subTo(x, y); y.rShiftTo(1, y); } } if (g > 0) y.lShiftTo(g, y); return y; } // protected BigInteger.prototype.copyTo = bnpCopyTo; BigInteger.prototype.fromInt = bnpFromInt; BigInteger.prototype.fromString = bnpFromString; BigInteger.prototype.fromByteArray = bnpFromByteArray; BigInteger.prototype.fromBuffer = bnpFromBuffer; BigInteger.prototype.clamp = bnpClamp; BigInteger.prototype.dlShiftTo = bnpDLShiftTo; BigInteger.prototype.drShiftTo = bnpDRShiftTo; BigInteger.prototype.lShiftTo = bnpLShiftTo; BigInteger.prototype.rShiftTo = bnpRShiftTo; BigInteger.prototype.subTo = bnpSubTo; BigInteger.prototype.multiplyTo = bnpMultiplyTo; BigInteger.prototype.squareTo = bnpSquareTo; BigInteger.prototype.divRemTo = bnpDivRemTo; BigInteger.prototype.invDigit = bnpInvDigit; BigInteger.prototype.isEven = bnpIsEven; BigInteger.prototype.exp = bnpExp; BigInteger.prototype.chunkSize = bnpChunkSize; BigInteger.prototype.toRadix = bnpToRadix; BigInteger.prototype.fromRadix = bnpFromRadix; BigInteger.prototype.bitwiseTo = bnpBitwiseTo; BigInteger.prototype.addTo = bnpAddTo; BigInteger.prototype.dMultiply = bnpDMultiply; BigInteger.prototype.dAddOffset = bnpDAddOffset; BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; // public BigInteger.prototype.toString = bnToString; BigInteger.prototype.negate = bnNegate; BigInteger.prototype.abs = bnAbs; BigInteger.prototype.compareTo = bnCompareTo; BigInteger.prototype.bitLength = bnBitLength; BigInteger.prototype.mod = bnMod; BigInteger.prototype.clone = bnClone; BigInteger.prototype.intValue = bnIntValue; BigInteger.prototype.signum = bnSigNum; BigInteger.prototype.toByteArray = bnToByteArray; BigInteger.prototype.toBuffer = bnToBuffer; BigInteger.prototype.equals = bnEquals; BigInteger.prototype.min = bnMin; BigInteger.prototype.max = bnMax; BigInteger.prototype.and = bnAnd; BigInteger.prototype.or = bnOr; BigInteger.prototype.xor = bnXor; BigInteger.prototype.not = bnNot; BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; BigInteger.prototype.add = bnAdd; BigInteger.prototype.subtract = bnSubtract; BigInteger.prototype.multiply = bnMultiply; BigInteger.prototype.divide = bnDivide; BigInteger.prototype.remainder = bnRemainder; BigInteger.prototype.modPow = bnModPow; BigInteger.prototype.pow = bnPow; BigInteger.prototype.gcd = bnGCD; BigInteger.int2char = int2char; // "constants" BigInteger.ZERO = nbv(0); BigInteger.ONE = nbv(1); // JSBN-specific extension BigInteger.prototype.square = bnSquare; //BigInteger interfaces not implemented in jsbn: //BigInteger(int signum, byte[] magnitude) //double doubleValue() //float floatValue() //int hashCode() //long longValue() //static BigInteger valueOf(long val) module.exports = BigInteger; }).call(this,require("buffer").Buffer) },{"../utils":150,"buffer":178}],147:[function(require,module,exports){ /* * RSA Encryption / Decryption with PKCS1 v2 Padding. * * ~MIT License~ * Contributors: * Tom Wu, rzcoder, frosty00 */ var _ = require('../utils')._; var BigInteger = require('./jsbn.js'); var utils = require('../utils.js'); var schemes = require('../schemes/schemes.js'); //exports.BigInteger = BigInteger; module.exports.Key = (function () { /** * RSA key constructor * * n - modulus * e - publicExponent * d - privateExponent * p - prime1 * q - prime2 * dmp1 - exponent1 -- d mod (p1) * dmq1 - exponent2 -- d mod (q-1) * coeff - coefficient -- (inverse of q) mod p */ function RSAKey() { this.n = null; this.e = 0; this.d = null; this.p = null; this.q = null; this.dmp1 = null; this.dmq1 = null; this.coeff = null; } RSAKey.prototype.setOptions = function (options) { var signingSchemeProvider = schemes[options.signingScheme]; var encryptionSchemeProvider = schemes[options.encryptionScheme]; if (signingSchemeProvider === encryptionSchemeProvider) { this.signingScheme = this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options); } else { this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options); this.signingScheme = signingSchemeProvider.makeScheme(this, options); } }; /** * Set the private key fields N, e, d and CRT params from buffers * * @param N * @param E * @param D * @param P * @param Q * @param DP * @param DQ * @param C */ RSAKey.prototype.setPrivate = function (N, E, D, P, Q, DP, DQ, C) { if (N && E && D && N.length > 0 && (_.isNumber(E) || E.length > 0) && D.length > 0) { this.n = new BigInteger(N); this.e = _.isNumber(E) ? E : utils.get32IntFromBuffer(E, 0); this.d = new BigInteger(D); if (P && Q && DP && DQ && C) { this.p = new BigInteger(P); this.q = new BigInteger(Q); this.dmp1 = new BigInteger(DP); this.dmq1 = new BigInteger(DQ); this.coeff = new BigInteger(C); } else { // TODO: re-calculate any missing CRT params } this.$$recalculateCache(); } else { throw Error("Invalid RSA private key"); } }; /** * private * Perform raw private operation on "x": return x^d (mod n) * * @param x * @returns {*} */ RSAKey.prototype.$doPrivate = function (x) { if (this.p || this.q) { return x.modPow(this.d, this.n); } // TODO: re-calculate any missing CRT params var xp = x.mod(this.p).modPow(this.dmp1, this.p); var xq = x.mod(this.q).modPow(this.dmq1, this.q); while (xp.compareTo(xq) < 0) { xp = xp.add(this.p); } return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); }; RSAKey.prototype.sign = function (buffer) { return this.signingScheme.sign.apply(this.signingScheme, arguments); }; /** * Check if key pair contains private key */ RSAKey.prototype.isPrivate = function () { return this.n && this.e && this.d || false; }; Object.defineProperty(RSAKey.prototype, 'keySize', { get: function () { return this.cache.keyBitLength; } }); Object.defineProperty(RSAKey.prototype, 'encryptedDataLength', { get: function () { return this.cache.keyByteLength; } }); Object.defineProperty(RSAKey.prototype, 'maxMessageLength', { get: function () { return this.encryptionScheme.maxMessageLength(); } }); /** * Caching key data */ RSAKey.prototype.$$recalculateCache = function () { this.cache = this.cache || {}; // Bit & byte length this.cache.keyBitLength = this.n.bitLength(); this.cache.keyByteLength = (this.cache.keyBitLength + 6) >> 3; }; return RSAKey; })(); },{"../schemes/schemes.js":149,"../utils":150,"../utils.js":150,"./jsbn.js":146}],148:[function(require,module,exports){ (function (Buffer){ /** * PKCS1 padding and signature scheme */ var BigInteger = require('../libs/jsbn'); var CryptoJS = require('../../crypto-js/crypto-js'); var constants = { RSA_NO_PADDING: 3 } require('constants'); var SIGN_INFO_HEAD = { md2: Buffer.from('3020300c06082a864886f70d020205000410', 'hex'), md5: Buffer.from('3020300c06082a864886f70d020505000410', 'hex'), sha1: Buffer.from('3021300906052b0e03021a05000414', 'hex'), sha224: Buffer.from('302d300d06096086480165030402040500041c', 'hex'), sha256: Buffer.from('3031300d060960864801650304020105000420', 'hex'), sha384: Buffer.from('3041300d060960864801650304020205000430', 'hex'), sha512: Buffer.from('3051300d060960864801650304020305000440', 'hex'), ripemd160: Buffer.from('3021300906052b2403020105000414', 'hex'), rmd160: Buffer.from('3021300906052b2403020105000414', 'hex') }; var SIGN_ALG_TO_HASH_ALIASES = { 'ripemd160': 'rmd160' }; var DEFAULT_HASH_FUNCTION = 'sha256'; module.exports = { isEncryption: true, isSignature: true }; module.exports.makeScheme = function (key, options) { function Scheme(key, options) { this.key = key; this.options = options; } Scheme.prototype.maxMessageLength = function () { if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { return this.key.encryptedDataLength; } return this.key.encryptedDataLength - 11; }; /** * Unpad input Buffer and, if valid, return the Buffer object * alg: PKCS#1 (type 2, random) * @param buffer * @returns {Buffer} */ Scheme.prototype.encUnPad = function (buffer, options) { options = options || {}; var i = 0; if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { //RSA_NO_PADDING treated like JAVA left pad with zero character var unPad; if (typeof buffer.lastIndexOf == "function") { //patch for old node version unPad = buffer.slice(buffer.lastIndexOf('\0') + 1, buffer.length); } else { unPad = buffer.slice(String.prototype.lastIndexOf.call(buffer, '\0') + 1, buffer.length); } return unPad; } if (buffer.length < 4) { return null; } /* Type 1: zeros padding for private key decrypt */ if (options.type === 1) { if (buffer[0] !== 0 && buffer[1] !== 1) { return null; } i = 3; while (buffer[i] !== 0) { if (buffer[i] != 0xFF || ++i >= buffer.length) { return null; } } } else { /* random padding for public key decrypt */ if (buffer[0] !== 0 && buffer[1] !== 2) { return null; } i = 3; while (buffer[i] !== 0) { if (++i >= buffer.length) { return null; } } } return buffer.slice(i + 1, buffer.length); }; Scheme.prototype.sign = function (buffer) { var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION; if (this.options.environment === 'browser') { hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm; var hasher = CryptoJS[hashAlgorithm.toUpperCase()] (buffer.toString()); var asBuffer = wordArrayToBuffer(hasher) var paddedHash = this.pkcs1pad(asBuffer, hashAlgorithm); var res = this.key.$doPrivate(new BigInteger(paddedHash)).toBuffer(this.key.encryptedDataLength); return res; } else { throw new Error ('CCXT only supports browser mode :P'); } }; /** * PKCS#1 pad input buffer to max data length * @param hashBuf * @param hashAlgorithm * @returns {*} */ Scheme.prototype.pkcs1pad = function (hashBuf, hashAlgorithm) { var digest = SIGN_INFO_HEAD[hashAlgorithm]; if (!digest) { throw Error('Unsupported hash algorithm'); } var data = Buffer.concat([digest, hashBuf]); if (data.length + 10 > this.key.encryptedDataLength) { throw Error('Key is too short for signing algorithm (' + hashAlgorithm + ')'); } var filled = Buffer.alloc(this.key.encryptedDataLength - data.length - 1); filled.fill(0xff, 0, filled.length - 1); filled[0] = 1; filled[filled.length - 1] = 0; var res = Buffer.concat([filled, data]); return res; }; return new Scheme(key, options); }; // used to convert `CryptoJS` wordArrays into `crypto` hex buffers function wordToByteArray(word, length) { var ba = [], xFF = 0xFF; if (length > 0) ba.push(word >>> 24); if (length > 1) ba.push((word >>> 16) & xFF); if (length > 2) ba.push((word >>> 8) & xFF); if (length > 3) ba.push(word & xFF); return ba; } function wordArrayToBuffer(wordArray) { let length = undefined; if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) { length = wordArray.sigBytes; wordArray = wordArray.words; } else { throw Error('Argument not a wordArray') } const result = [] let bytes = [] let i = 0; while (length > 0) { bytes = wordToByteArray(wordArray[i], Math.min(4, length)); length -= bytes.length; result.push(bytes); i++; } return new Buffer.from([].concat.apply([], result), 'hex'); } }).call(this,require("buffer").Buffer) },{"../../crypto-js/crypto-js":120,"../libs/jsbn":146,"buffer":178,"constants":180}],149:[function(require,module,exports){ module.exports = { pkcs1: require('./pkcs1'), /** * Check if scheme has padding methods * @param scheme {string} * @returns {Boolean} */ isEncryption: function (scheme) { return module.exports[scheme] && module.exports[scheme].isEncryption; }, /** * Check if scheme has sign/verify methods * @param scheme {string} * @returns {Boolean} */ isSignature: function (scheme) { return module.exports[scheme] && module.exports[scheme].isSignature; } }; },{"./pkcs1":148}],150:[function(require,module,exports){ /* * Utils functions * */ /** * Break string str each maxLen symbols * @param str * @param maxLen * @returns {string} */ module.exports.linebrk = function (str, maxLen) { var res = ''; var i = 0; while (i + maxLen < str.length) { res += str.substring(i, i + maxLen) + "\n"; i += maxLen; } return res + str.substring(i, str.length); }; /** * Trying get a 32-bit unsigned integer from the partial buffer * @param buffer * @param offset * @returns {Number} */ module.exports.get32IntFromBuffer = function (buffer, offset) { offset = offset || 0; var size = 0; if ((size = buffer.length - offset) > 0) { if (size >= 4) { return buffer.readUInt32BE(offset); } else { var res = 0; for (var i = offset + size, d = 0; i > offset; i--, d += 2) { res += buffer[i - 1] * Math.pow(16, d); } return res; } } else { return NaN; } }; module.exports._ = { isObject: function (value) { var type = typeof value; return !!value && (type == 'object' || type == 'function'); }, isString: function (value) { return typeof value == 'string' || value instanceof String; }, isNumber: function (value) { return typeof value == 'number' || !isNaN(parseFloat(value)) && isFinite(value); }, /** * Returns copy of `obj` without `removeProp` field. * @param obj * @param removeProp * @returns Object */ omit: function (obj, removeProp) { var newObj = {}; for (var prop in obj) { if (!obj.hasOwnProperty(prop) || prop === removeProp) { continue; } newObj[prop] = obj[prop]; } return newObj; } }; /** * Strips everything around the opening and closing lines, including the lines * themselves. */ module.exports.trimSurroundingText = function (data, opening, closing) { var trimStartIndex = 0; var trimEndIndex = data.length; var openingBoundaryIndex = data.indexOf(opening); if (openingBoundaryIndex >= 0) { trimStartIndex = openingBoundaryIndex + opening.length; } var closingBoundaryIndex = data.indexOf(closing, openingBoundaryIndex); if (closingBoundaryIndex >= 0) { trimEndIndex = closingBoundaryIndex; } return data.substring(trimStartIndex, trimEndIndex); } },{}],151:[function(require,module,exports){ 'use strict'; var replace = String.prototype.replace; var percentTwenties = /%20/g; module.exports = { 'default': 'RFC3986', formatters: { RFC1738: function (value) { return replace.call(value, percentTwenties, '+'); }, RFC3986: function (value) { return value; } }, RFC1738: 'RFC1738', RFC3986: 'RFC3986' }; },{}],152:[function(require,module,exports){ 'use strict'; var stringify = require('./stringify'); var parse = require('./parse'); var formats = require('./formats'); module.exports = { formats: formats, parse: parse, stringify: stringify }; },{"./formats":151,"./parse":153,"./stringify":154}],153:[function(require,module,exports){ 'use strict'; var utils = require('./utils'); var has = Object.prototype.hasOwnProperty; var defaults = { allowDots: false, allowPrototypes: false, arrayLimit: 20, charset: 'utf-8', charsetSentinel: false, comma: false, decoder: utils.decode, delimiter: '&', depth: 5, ignoreQueryPrefix: false, interpretNumericEntities: false, parameterLimit: 1000, parseArrays: true, plainObjects: false, strictNullHandling: false }; var interpretNumericEntities = function (str) { return str.replace(/&#(\d+);/g, function ($0, numberStr) { return String.fromCharCode(parseInt(numberStr, 10)); }); }; // This is what browsers will submit when the ✓ character occurs in an // application/x-www-form-urlencoded body and the encoding of the page containing // the form is iso-8859-1, or when the submitted form has an accept-charset // attribute of iso-8859-1. Presumably also with other charsets that do not contain // the ✓ character, such as us-ascii. var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('✓') // These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded. var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓') var parseValues = function parseQueryStringValues(str, options) { var obj = {}; var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str; var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit; var parts = cleanStr.split(options.delimiter, limit); var skipIndex = -1; // Keep track of where the utf8 sentinel was found var i; var charset = options.charset; if (options.charsetSentinel) { for (i = 0; i < parts.length; ++i) { if (parts[i].indexOf('utf8=') === 0) { if (parts[i] === charsetSentinel) { charset = 'utf-8'; } else if (parts[i] === isoSentinel) { charset = 'iso-8859-1'; } skipIndex = i; i = parts.length; // The eslint settings do not allow break; } } } for (i = 0; i < parts.length; ++i) { if (i === skipIndex) { continue; } var part = parts[i]; var bracketEqualsPos = part.indexOf(']='); var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1; var key, val; if (pos === -1) { key = options.decoder(part, defaults.decoder, charset); val = options.strictNullHandling ? null : ''; } else { key = options.decoder(part.slice(0, pos), defaults.decoder, charset); val = options.decoder(part.slice(pos + 1), defaults.decoder, charset); } if (val && options.interpretNumericEntities && charset === 'iso-8859-1') { val = interpretNumericEntities(val); } if (val && options.comma && val.indexOf(',') > -1) { val = val.split(','); } if (has.call(obj, key)) { obj[key] = utils.combine(obj[key], val); } else { obj[key] = val; } } return obj; }; var parseObject = function (chain, val, options) { var leaf = val; for (var i = chain.length - 1; i >= 0; --i) { var obj; var root = chain[i]; if (root === '[]' && options.parseArrays) { obj = [].concat(leaf); } else { obj = options.plainObjects ? Object.create(null) : {}; var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root; var index = parseInt(cleanRoot, 10); if (!options.parseArrays && cleanRoot === '') { obj = { 0: leaf }; } else if ( !isNaN(index) && root !== cleanRoot && String(index) === cleanRoot && index >= 0 && (options.parseArrays && index <= options.arrayLimit) ) { obj = []; obj[index] = leaf; } else { obj[cleanRoot] = leaf; } } leaf = obj; } return leaf; }; var parseKeys = function parseQueryStringKeys(givenKey, val, options) { if (!givenKey) { return; } // Transform dot notation to bracket notation var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey; // The regex chunks var brackets = /(\[[^[\]]*])/; var child = /(\[[^[\]]*])/g; // Get the parent var segment = brackets.exec(key); var parent = segment ? key.slice(0, segment.index) : key; // Stash the parent if it exists var keys = []; if (parent) { // If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties if (!options.plainObjects && has.call(Object.prototype, parent)) { if (!options.allowPrototypes) { return; } } keys.push(parent); } // Loop through children appending to the array until we hit depth var i = 0; while ((segment = child.exec(key)) !== null && i < options.depth) { i += 1; if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) { if (!options.allowPrototypes) { return; } } keys.push(segment[1]); } // If there's a remainder, just add whatever is left if (segment) { keys.push('[' + key.slice(segment.index) + ']'); } return parseObject(keys, val, options); }; var normalizeParseOptions = function normalizeParseOptions(opts) { if (!opts) { return defaults; } if (opts.decoder !== null && opts.decoder !== undefined && typeof opts.decoder !== 'function') { throw new TypeError('Decoder has to be a function.'); } if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') { throw new Error('The charset option must be either utf-8, iso-8859-1, or undefined'); } var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset; return { allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots, allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes, arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit, charset: charset, charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel, comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma, decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder, delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter, depth: typeof opts.depth === 'number' ? opts.depth : defaults.depth, ignoreQueryPrefix: opts.ignoreQueryPrefix === true, interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities, parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit, parseArrays: opts.parseArrays !== false, plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects, strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling }; }; module.exports = function (str, opts) { var options = normalizeParseOptions(opts); if (str === '' || str === null || typeof str === 'undefined') { return options.plainObjects ? Object.create(null) : {}; } var tempObj = typeof str === 'string' ? parseValues(str, options) : str; var obj = options.plainObjects ? Object.create(null) : {}; // Iterate over the keys and setup the new object var keys = Object.keys(tempObj); for (var i = 0; i < keys.length; ++i) { var key = keys[i]; var newObj = parseKeys(key, tempObj[key], options); obj = utils.merge(obj, newObj, options); } return utils.compact(obj); }; },{"./utils":155}],154:[function(require,module,exports){ 'use strict'; var utils = require('./utils'); var formats = require('./formats'); var has = Object.prototype.hasOwnProperty; var arrayPrefixGenerators = { brackets: function brackets(prefix) { // eslint-disable-line func-name-matching return prefix + '[]'; }, comma: 'comma', indices: function indices(prefix, key) { // eslint-disable-line func-name-matching return prefix + '[' + key + ']'; }, repeat: function repeat(prefix) { // eslint-disable-line func-name-matching return prefix; } }; var isArray = Array.isArray; var push = Array.prototype.push; var pushToArray = function (arr, valueOrArray) { push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]); }; var toISO = Date.prototype.toISOString; var defaults = { addQueryPrefix: false, allowDots: false, charset: 'utf-8', charsetSentinel: false, delimiter: '&', encode: true, encoder: utils.encode, encodeValuesOnly: false, formatter: formats.formatters[formats['default']], // deprecated indices: false, serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching return toISO.call(date); }, skipNulls: false, strictNullHandling: false }; var stringify = function stringify( // eslint-disable-line func-name-matching object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots, serializeDate, formatter, encodeValuesOnly, charset ) { var obj = object; if (typeof filter === 'function') { obj = filter(prefix, obj); } else if (obj instanceof Date) { obj = serializeDate(obj); } else if (generateArrayPrefix === 'comma' && isArray(obj)) { obj = obj.join(','); } if (obj === null) { if (strictNullHandling) { return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset) : prefix; } obj = ''; } if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) { if (encoder) { var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset); return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset))]; } return [formatter(prefix) + '=' + formatter(String(obj))]; } var values = []; if (typeof obj === 'undefined') { return values; } var objKeys; if (isArray(filter)) { objKeys = filter; } else { var keys = Object.keys(obj); objKeys = sort ? keys.sort(sort) : keys; } for (var i = 0; i < objKeys.length; ++i) { var key = objKeys[i]; if (skipNulls && obj[key] === null) { continue; } if (isArray(obj)) { pushToArray(values, stringify( obj[key], typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots, serializeDate, formatter, encodeValuesOnly, charset )); } else { pushToArray(values, stringify( obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots, serializeDate, formatter, encodeValuesOnly, charset )); } } return values; }; var normalizeStringifyOptions = function normalizeStringifyOptions(opts) { if (!opts) { return defaults; } if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') { throw new TypeError('Encoder has to be a function.'); } var charset = opts.charset || defaults.charset; if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') { throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'); } var format = formats['default']; if (typeof opts.format !== 'undefined') { if (!has.call(formats.formatters, opts.format)) { throw new TypeError('Unknown format option provided.'); } format = opts.format; } var formatter = formats.formatters[format]; var filter = defaults.filter; if (typeof opts.filter === 'function' || isArray(opts.filter)) { filter = opts.filter; } return { addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix, allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots, charset: charset, charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel, delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter, encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode, encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder, encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly, filter: filter, formatter: formatter, serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate, skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls, sort: typeof opts.sort === 'function' ? opts.sort : null, strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling }; }; module.exports = function (object, opts) { var obj = object; var options = normalizeStringifyOptions(opts); var objKeys; var filter; if (typeof options.filter === 'function') { filter = options.filter; obj = filter('', obj); } else if (isArray(options.filter)) { filter = options.filter; objKeys = filter; } var keys = []; if (typeof obj !== 'object' || obj === null) { return ''; } var arrayFormat; if (opts && opts.arrayFormat in arrayPrefixGenerators) { arrayFormat = opts.arrayFormat; } else if (opts && 'indices' in opts) { arrayFormat = opts.indices ? 'indices' : 'repeat'; } else { arrayFormat = 'indices'; } var generateArrayPrefix = arrayPrefixGenerators[arrayFormat]; if (!objKeys) { objKeys = Object.keys(obj); } if (options.sort) { objKeys.sort(options.sort); } for (var i = 0; i < objKeys.length; ++i) { var key = objKeys[i]; if (options.skipNulls && obj[key] === null) { continue; } pushToArray(keys, stringify( obj[key], key, generateArrayPrefix, options.strictNullHandling, options.skipNulls, options.encode ? options.encoder : null, options.filter, options.sort, options.allowDots, options.serializeDate, options.formatter, options.encodeValuesOnly, options.charset )); } var joined = keys.join(options.delimiter); var prefix = options.addQueryPrefix === true ? '?' : ''; if (options.charsetSentinel) { if (options.charset === 'iso-8859-1') { // encodeURIComponent('✓'), the "numeric entity" representation of a checkmark prefix += 'utf8=%26%2310003%3B&'; } else { // encodeURIComponent('✓') prefix += 'utf8=%E2%9C%93&'; } } return joined.length > 0 ? prefix + joined : ''; }; },{"./formats":151,"./utils":155}],155:[function(require,module,exports){ 'use strict'; var has = Object.prototype.hasOwnProperty; var isArray = Array.isArray; var hexTable = (function () { var array = []; for (var i = 0; i < 256; ++i) { array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); } return array; }()); var compactQueue = function compactQueue(queue) { while (queue.length > 1) { var item = queue.pop(); var obj = item.obj[item.prop]; if (isArray(obj)) { var compacted = []; for (var j = 0; j < obj.length; ++j) { if (typeof obj[j] !== 'undefined') { compacted.push(obj[j]); } } item.obj[item.prop] = compacted; } } }; var arrayToObject = function arrayToObject(source, options) { var obj = options && options.plainObjects ? Object.create(null) : {}; for (var i = 0; i < source.length; ++i) { if (typeof source[i] !== 'undefined') { obj[i] = source[i]; } } return obj; }; var merge = function merge(target, source, options) { if (!source) { return target; } if (typeof source !== 'object') { if (isArray(target)) { target.push(source); } else if (target && typeof target === 'object') { if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) { target[source] = true; } } else { return [target, source]; } return target; } if (!target || typeof target !== 'object') { return [target].concat(source); } var mergeTarget = target; if (isArray(target) && !isArray(source)) { mergeTarget = arrayToObject(target, options); } if (isArray(target) && isArray(source)) { source.forEach(function (item, i) { if (has.call(target, i)) { var targetItem = target[i]; if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') { target[i] = merge(targetItem, item, options); } else { target.push(item); } } else { target[i] = item; } }); return target; } return Object.keys(source).reduce(function (acc, key) { var value = source[key]; if (has.call(acc, key)) { acc[key] = merge(acc[key], value, options); } else { acc[key] = value; } return acc; }, mergeTarget); }; var assign = function assignSingleSource(target, source) { return Object.keys(source).reduce(function (acc, key) { acc[key] = source[key]; return acc; }, target); }; var decode = function (str, decoder, charset) { var strWithoutPlus = str.replace(/\+/g, ' '); if (charset === 'iso-8859-1') { // unescape never throws, no try...catch needed: return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape); } // utf-8 try { return decodeURIComponent(strWithoutPlus); } catch (e) { return strWithoutPlus; } }; var encode = function encode(str, defaultEncoder, charset) { // This code was originally written by Brian White (mscdex) for the io.js core querystring library. // It has been adapted here for stricter adherence to RFC 3986 if (str.length === 0) { return str; } var string = typeof str === 'string' ? str : String(str); if (charset === 'iso-8859-1') { return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) { return '%26%23' + parseInt($0.slice(2), 16) + '%3B'; }); } var out = ''; for (var i = 0; i < string.length; ++i) { var c = string.charCodeAt(i); if ( c === 0x2D // - || c === 0x2E // . || c === 0x5F // _ || c === 0x7E // ~ || (c >= 0x30 && c <= 0x39) // 0-9 || (c >= 0x41 && c <= 0x5A) // a-z || (c >= 0x61 && c <= 0x7A) // A-Z ) { out += string.charAt(i); continue; } if (c < 0x80) { out = out + hexTable[c]; continue; } if (c < 0x800) { out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]); continue; } if (c < 0xD800 || c >= 0xE000) { out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]); continue; } i += 1; c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]; } return out; }; var compact = function compact(value) { var queue = [{ obj: { o: value }, prop: 'o' }]; var refs = []; for (var i = 0; i < queue.length; ++i) { var item = queue[i]; var obj = item.obj[item.prop]; var keys = Object.keys(obj); for (var j = 0; j < keys.length; ++j) { var key = keys[j]; var val = obj[key]; if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { queue.push({ obj: obj, prop: key }); refs.push(val); } } } compactQueue(queue); return value; }; var isRegExp = function isRegExp(obj) { return Object.prototype.toString.call(obj) === '[object RegExp]'; }; var isBuffer = function isBuffer(obj) { if (!obj || typeof obj !== 'object') { return false; } return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); }; var combine = function combine(a, b) { return [].concat(a, b); }; module.exports = { arrayToObject: arrayToObject, assign: assign, combine: combine, compact: compact, decode: decode, encode: encode, isBuffer: isBuffer, isRegExp: isRegExp, merge: merge }; },{}],156:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ArgumentsRequired, AuthenticationError, ExchangeError, InsufficientFunds, OrderNotFound, PermissionDenied, BadRequest, DDoSProtection } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class stex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'stex', 'name': 'STEX', // formerly known as stocks.exchange 'countries': [ 'EE' ], // Estonia 'rateLimit': 500, // https://help.stex.com/en/articles/2815043-api-3-rate-limits 'certified': false, // new metainfo interface 'has': { 'CORS': false, 'createMarketOrder': false, // limit orders only 'fetchCurrencies': true, 'fetchMarkets': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchOrderBook': true, 'fetchOHLCV': true, 'fetchBalance': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchMyTrades': true, 'fetchOrderTrades': true, 'fetchDepositAddress': true, 'createDepositAddress': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'withdraw': true, 'fetchFundingFees': true, }, 'version': 'v3', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/69680782-03fd0b80-10bd-11ea-909e-7f603500e9cc.jpg', 'api': 'https://api3.stex.com', 'www': 'https://www.stex.com', 'doc': [ 'https://help.stex.com/en/collections/1593608-api-v3-documentation', ], 'fees': 'https://app.stex.com/en/pairs-specification', 'referral': 'https://app.stex.com?ref=36416021', }, 'requiredCredentials': { 'apiKey': false, 'secret': false, 'token': true, }, 'timeframes': { '1m': '1', '5m': '5', '30m': '30', '1h': '60', '4h': '240', '12h': '720', '1d': '1D', // default }, 'api': { 'public': { 'get': [ 'currencies', // Available Currencies 'currencies/{currencyId}', // Get currency info 'markets', // Available markets 'pairs-groups', // Available currency pairs groups (as displayed at stex trading page) 'currency_pairs/list/{code}', // Available currency pairs 'currency_pairs/group/{currencyPairGroupId}', // Available currency pairs for a given group 'currency_pairs/{currencyPairId}', // Get currency pair information 'ticker', // Tickers list for all currency pairs 'ticker/{currencyPairId}', // Ticker for currency pair 'trades/{currencyPairId}', // Trades for given currency pair 'orderbook/{currencyPairId}', // Orderbook for given currency pair 'chart/{currencyPairId}/{candlesType}', // A list of candles for given currency pair 'deposit-statuses', // Available Deposit Statuses 'deposit-statuses/{statusId}', // Get deposit status info 'withdrawal-statuses', // Available Withdrawal Statuses 'withdrawal-statuses/{statusId}', // Get status info 'ping', // Test API is working and get server time 'mobile-versions', // Shows the official mobile applications data ], }, 'trading': { 'get': [ 'fees/{currencyPairId}', // Returns the user's fees for a given currency pair 'orders', // List your currently open orders 'orders/{currencyPairId}', // List your currently open orders for given currency pair 'order/{orderId}', // Get a single order ], 'post': [ 'orders/{currencyPairId}', // Create new order and put it to the orders processing queue ], 'delete': [ 'orders', // Delete all active orders 'orders/{currencyPairId}', // Delete active orders for given currency pair 'order/{orderId}', // Cancel order ], }, 'reports': { 'get': [ 'orders', // Get past orders 'orders/{orderId}', // Get specified order details 'trades/{currencyPairId}', // Get a list of user trades according to request parameters 'background/{listMode}', // Get reports list for category 'background/{id}', // Get some report info 'background/download/{id}', // Get file by id ], 'post': [ 'background/create', // Create new report ], 'delete': [ 'background/{id}', // Remove report by id ], }, 'profile': { 'get': [ 'info', // Account information 'wallets', // Get a list of user wallets 'wallets/{walletId}', // Single wallet information 'wallets/address/{walletId}', // Get deposit address for given wallet 'deposits', // Get a list of deposits made by user 'deposits/{id}', // Get deposit by id 'withdrawals', // Get a list of withdrawals made by user 'withdrawals/{id}', // Get withdrawal by id 'notifications', // Get notifications 'favorite/currency_pairs', // Get favorite currency pairs 'token-scopes', // Get current token scopes ], 'post': [ 'wallets/burn/{walletId}', // Burns the given wallet 'wallets/{currencyId}', // Create a wallet for given currency 'wallets/address/{walletId}', // Create new deposit address 'withdraw', // Create withdrawal request 'referral/program', // Create referral program 'referral/insert/{code}', // Insert referral code 'referral/bonus_transfer/{currencyId}', // Transfer referral bonuses balance to main balance for given currency ], 'put': [ 'profile/favorite/currency_pairs/set', // Set favorite currency pairs ], 'delete': [ 'profile/withdraw/{withdrawalId}', // Cancel unconfirmed withdrawal ], }, 'verification': { 'get': [ 'verification/countries', // Countries list, beta 'verification/stex', // Get information about your KYC, beta ], 'post': [ 'verification/stex', // Update information regarding of your KYC verification, beta ], }, 'settings': { 'get': [ 'notifications/{event}', // User event notification settings 'notifications', // User events notification settings ], 'put': [ 'notifications', // Set notification settings 'notifications/set', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.002, 'maker': 0.002, }, }, 'commonCurrencies': { 'BHD': 'Bithold', }, 'options': { 'parseOrderToPrecision': false, }, 'exceptions': { 'exact': { // {"success":false,"message":"Wrong parameters","errors":{"candleType":["Invalid Candle Type!"]}} // {"success":false,"message":"Wrong parameters","errors":{"time":["timeStart or timeEnd is less then 1"]}} 'Wrong parameters': BadRequest, 'Unauthenticated.': AuthenticationError, // {"message":"Unauthenticated."} 'Server Error': ExchangeError, // { "message": "Server Error" } 'This feature is only enabled for users verifies by Cryptonomica': PermissionDenied, // {"success":false,"message":"This feature is only enabled for users verifies by Cryptonomica"} 'Too Many Attempts.': DDoSProtection, // { "message": "Too Many Attempts." } }, 'broad': { 'Not enough': InsufficientFunds, // {"success":false,"message":"Not enough ETH"} }, }, }); } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencies (params); // // { // "success":true, // "data":[ // { // "id":1, // "code":"BTC", // "name":"Bitcoin", // "active":true, // "delisted":false, // "precision":8, // "minimum_tx_confirmations":1, // "minimum_withdrawal_amount":"0.00200000", // "minimum_deposit_amount":"0.00000000", // "deposit_fee_currency_id":1, // "deposit_fee_currency_code":"BTC", // "deposit_fee_const":"0.00000000", // "deposit_fee_percent":"0.00000000", // "withdrawal_fee_currency_id":1, // "withdrawal_fee_currency_code":"BTC", // "withdrawal_fee_const":"0.00100000", // "withdrawal_fee_percent":"0.00000000", // "block_explorer_url":"https:\/\/blockchain.info\/tx\/", // "protocol_specific_settings":null // }, // ] // } // const result = {}; const currencies = this.safeValue (response, 'data', []); for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString (currency, 'id'); const numericId = this.safeInteger (currency, 'id'); // todo: will need to rethink the fees // to add support for multiple withdrawal/deposit methods and // differentiated fees for each particular method const code = this.safeCurrencyCode (this.safeString (currency, 'code')); const precision = this.safeInteger (currency, 'precision'); const fee = this.safeFloat (currency, 'withdrawal_fee_const'); // todo: redesign const active = this.safeValue (currency, 'active', true); result[code] = { 'id': id, 'numericId': numericId, 'code': code, 'info': currency, 'type': undefined, 'name': this.safeString (currency, 'name'), 'active': active, 'fee': fee, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision), 'max': undefined }, 'price': { 'min': Math.pow (10, -precision), 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, 'deposit': { 'min': this.safeFloat (currency, 'minimum_deposit_amount'), 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'minimum_withdrawal_amount'), 'max': undefined, }, }, }; } return result; } async fetchMarkets (params = {}) { const request = { 'code': 'ALL', }; const response = await this.publicGetCurrencyPairsListCode (this.extend (request, params)); // // { // "success":true, // "data":[ // { // "id":935, // "currency_id":662, // "currency_code":"ABET", // "currency_name":"Altbet", // "market_currency_id":1, // "market_code":"BTC", // "market_name":"Bitcoin", // "min_order_amount":"0.00000010", // "min_buy_price":"0.00000001", // "min_sell_price":"0.00000001", // "buy_fee_percent":"0.20000000", // "sell_fee_percent":"0.20000000", // "active":true, // "delisted":false, // "pair_message":"", // "currency_precision":8, // "market_precision":8, // "symbol":"ABET_BTC", // "group_name":"BTC", // "group_id":1 // } // ] // } // const result = []; const markets = this.safeValue (response, 'data', []); for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'id'); const numericId = this.safeInteger (market, 'id'); const baseId = this.safeString (market, 'currency_id'); const quoteId = this.safeString (market, 'market_currency_id'); const baseNumericId = this.safeInteger (market, 'currency_id'); const quoteNumericId = this.safeInteger (market, 'market_currency_id'); const base = this.safeCurrencyCode (this.safeString (market, 'currency_code')); const quote = this.safeCurrencyCode (this.safeString (market, 'market_code')); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'currency_precision'), 'price': this.safeInteger (market, 'market_precision'), }; const active = this.safeValue (market, 'active'); const minBuyPrice = this.safeFloat (market, 'min_buy_price'); const minSellPrice = this.safeFloat (market, 'min_sell_price'); const minPrice = Math.max (minBuyPrice, minSellPrice); const buyFee = this.safeFloat (market, 'buy_fee_percent') / 100; const sellFee = this.safeFloat (market, 'sell_fee_percent') / 100; const fee = Math.max (buyFee, sellFee); result.push ({ 'id': id, 'numericId': numericId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'baseNumericId': baseNumericId, 'quoteNumericId': quoteNumericId, 'info': market, 'active': active, 'maker': fee, 'taker': fee, 'precision': precision, 'limits': { 'amount': { 'min': this.safeFloat (market, 'min_order_amount'), 'max': undefined, }, 'price': { 'min': minPrice, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPairId': market['id'], }; const response = await this.publicGetTickerCurrencyPairId (this.extend (request, params)); // // { // "success": true, // "data": { // "id": 2, // "amount_multiplier": 1, // "currency_code": "ETH", // "market_code": "BTC", // "currency_name": "Ethereum", // "market_name": "Bitcoin", // "symbol": "ETH_BTC", // "group_name": "BTC", // "group_id": 1, // "ask": "0.02069998", // "bid": "0.02028622", // "last": "0.02049224", // "open": "0.02059605", // "low": "0.01977744", // "high": "0.02097005", // "volume": "480.43248971", // "volumeQuote": "23491.29826130", // "count": "7384", // "fiatsRate": { // "USD": 7230.86, // "EUR": 6590.79, // "UAH": 173402, // "AUD": 10595.51, // "IDR": 101568085, // "CNY": 50752, // "KRW": 8452295, // "JPY": 784607, // "VND": 167315119, // "INR": 517596, // "GBP": 5607.25, // "CAD": 9602.63, // "BRL": 30472, // "RUB": 460718 // }, // "timestamp": 1574698235601 // } // } // const ticker = this.safeValue (response, 'data', {}); return this.parseTicker (ticker, market); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPairId': market['id'], }; if (limit !== undefined) { request['limit_bids'] = limit; // returns all if set to 0, default 100 request['limit_asks'] = limit; // returns all if set to 0, default 100 } const response = await this.publicGetOrderbookCurrencyPairId (this.extend (request, params)); // // { // "success": true, // "data": { // "ask": [ // { "currency_pair_id": 2, "amount": "2.17865373", "price": "0.02062917", "amount2": "0.04494382", "count": 1, "cumulative_amount": 2.17865373 }, // { "currency_pair_id": 2, "amount": "2.27521743", "price": "0.02062918", "amount2": "0.04693587", "count": 1, "cumulative_amount": 4.45387116 }, // { "currency_pair_id": 2, "amount": "1.26980049", "price": "0.02063170", "amount2": "0.02619814", "count": 1, "cumulative_amount": 5.72367165 }, // ], // "bid": [ // { "currency_pair_id": 2, "amount": "0.00978005", "price": "0.02057000", "amount2": "0.00020118", "count": 1, "cumulative_amount": 0.00978005 }, // { "currency_pair_id": 2, "amount": "0.00500000", "price": "0.02056000", "amount2": "0.00010280", "count": 1, "cumulative_amount": 0.01478005 }, // { "currency_pair_id": 2, "amount": "0.77679882", "price": "0.02054001", "amount2": "0.01595546", "count": 1, "cumulative_amount": 0.79157887 }, // ], // "ask_total_amount": 2555.749174609999, // "bid_total_amount": 29.180037330000005 // } // } // const orderbook = this.safeValue (response, 'data', {}); return this.parseOrderBook (orderbook, undefined, 'bid', 'ask', 'price', 'amount'); } parseTicker (ticker, market = undefined) { // // { // "id": 2, // "amount_multiplier": 1, // "currency_code": "ETH", // "market_code": "BTC", // "currency_name": "Ethereum", // "market_name": "Bitcoin", // "symbol": "ETH_BTC", // "group_name": "BTC", // "group_id": 1, // "ask": "0.02069998", // "bid": "0.02028622", // "last": "0.02049224", // "open": "0.02059605", // "low": "0.01977744", // "high": "0.02097005", // "volume": "480.43248971", // "volumeQuote": "23491.29826130", // "count": "7384", // "fiatsRate": { // "USD": 7230.86, // "EUR": 6590.79, // "UAH": 173402, // "AUD": 10595.51, // "IDR": 101568085, // "CNY": 50752, // "KRW": 8452295, // "JPY": 784607, // "VND": 167315119, // "INR": 517596, // "GBP": 5607.25, // "CAD": 9602.63, // "BRL": 30472, // "RUB": 460718 // }, // "timestamp": 1574698235601 // } // const timestamp = this.safeInteger (ticker, 'timestamp'); let symbol = undefined; let marketId = this.safeString (ticker, 'id'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { marketId = this.safeString (ticker, 'symbol'); if (marketId !== undefined) { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); const open = this.safeFloat (ticker, 'open'); let change = undefined; let percentage = undefined; if (last !== undefined) { if ((open !== undefined) && (open > 0)) { change = last - open; percentage = ((100 / open) * last) - 100; } } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, // previous day close 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volumeQuote'), 'quoteVolume': this.safeFloat (ticker, 'volume'), 'info': ticker, }; } parseTickers (tickers, symbols = undefined) { const result = []; for (let i = 0; i < tickers.length; i++) { result.push (this.parseTicker (tickers[i])); } return this.filterByArray (result, 'symbol', symbols); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker (params); // // { // "success":true, // "data":[ // { // "id":262, // "amount_multiplier":1, // "currency_code":"ARDR", // "market_code":"BTC", // "currency_name":"ARDOR", // "market_name":"Bitcoin", // "symbol":"ARDR_BTC", // "group_name":"BTC", // "group_id":1, // "ask":"0.00000630", // "bid":"0.00000613", // "last":"0.00000617", // "open":"0.00000620", // "low":"0.00000614", // "high":"0.00000630", // "volume":"30.37795305", // "volumeQuote":"4911487.01996544", // "count":"710", // "fiatsRate":{ // "USD":7230.86, // "EUR":6590.79, // "UAH":173402, // "AUD":10744.52, // "IDR":101568085, // "CNY":50752, // "KRW":8452295, // "JPY":784607, // "VND":167315119, // "INR":517596, // "GBP":5607.25, // "CAD":9602.63, // "BRL":30472, // "RUB":467358 // }, // "timestamp":1574698617304, // "group_position":1 // }, // ] // } // const tickers = this.safeValue (response, 'data', []); return this.parseTickers (tickers, symbols); } parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { // // { // "time": 1566086400000, // "close": 0.01895, // "open": 0.01812427, // "high": 0.0191588, // "low": 0.01807001, // "volume": 2588.597813750006 // } // return [ this.safeInteger (ohlcv, 'time'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPairId': market['id'], 'candlesType': this.timeframes[timeframe], // default 1d // 'timeStart': 1574709092, // unix timestamp in seconds, required // 'timeEnd': 1574709092, // unix timestamp in seconds, required // 'limit': 100, // default 100, optional // 'offset' 100, // optional, pagination within timerange }; if (limit === undefined) { limit = 100; } else { request['limit'] = limit; } const duration = this.parseTimeframe (timeframe); const timerange = limit * duration; if (since === undefined) { request['timeEnd'] = this.seconds (); request['timeStart'] = request['timeEnd'] - timerange; } else { request['timeStart'] = parseInt (since / 1000); request['timeEnd'] = this.sum (request['timeStart'], timerange); } const response = await this.publicGetChartCurrencyPairIdCandlesType (this.extend (request, params)); // // { // "success": true, // "data": [ // { // "time": 1566086400000, // "close": 0.01895, // "open": 0.01812427, // "high": 0.0191588, // "low": 0.01807001, // "volume": 2588.597813750006 // }, // ] // } // const ohlcvs = this.safeValue (response, 'data', []); return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // public fetchTrades // // { // "id": 35989317, // "price": "0.02033813", // "amount": "3.60000000", // "type": "BUY", // "timestamp": "1574713503" // } // // private fetchMyTrades, fetchClosedOrder, fetchOrderTrades // // { // "id": 658745, // "buy_order_id": 6587453, // "sell_order_id": 6587459, // "price": 0.012285, // "amount": 6.35, // "trade_type": "SELL", // "timestamp": "1538737692" // } // const id = this.safeString (trade, 'id'); const timestamp = this.safeTimestamp (trade, 'timestamp'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if ((price !== undefined) && (amount !== undefined)) { cost = price * amount; } let symbol = undefined; if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const side = this.safeStringLower2 (trade, 'type', 'trade_type'); return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': undefined, 'type': undefined, 'takerOrMaker': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPairId': market['id'], // 'sort': 'ASC', // ASC or DESC, default DESC // 'from': 1574709092, // unix timestamp, optional // 'till': 1574709092, // unix timestamp, optional // 'limit': 100, // default 100, optional // 'offset': 100, // optional }; if (limit !== undefined) { request['limit'] = limit; // currently limited to 100 or fewer } if (since !== undefined) { request['sort'] = 'ASC'; // needed to make the from param work request['from'] = parseInt (since / 1000); } const response = await this.publicGetTradesCurrencyPairId (this.extend (request, params)); // // { // "success": true, // "data": [ // { // "id": 35989317, // "price": "0.02033813", // "amount": "3.60000000", // "type": "BUY", // "timestamp": "1574713503" // }, // ] // } // const trades = this.safeValue (response, 'data', []); return this.parseTrades (trades, market, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); // await this.loadAccounts (); const response = await this.profileGetWallets (params); // // { // "success": true, // "data": [ // { // "id": null, // "currency_id": 665, // "delisted": false, // "disabled": false, // "disable_deposits": false, // "currency_code": "ORM", // "currency_name": "Orium", // "currency_type_id": 5, // "balance": "0", // "frozen_balance": "0", // "bonus_balance": "0", // "total_balance": "0", // "protocol_specific_settings": null, // "rates": { "BTC": "0.00000000020", "USD": "0.00000147" }, // }, // { // "id": null, // "currency_id": 272, // "delisted": false, // "disabled": false, // "disable_deposits": false, // "currency_code": "USDT", // "currency_name": "TetherUSD", // "currency_type_id": 23, // "balance": "0", // "frozen_balance": "0", // "bonus_balance": "0", // "total_balance": "0", // "protocol_specific_settings": [ // { "protocol_name": "OMNI", "protocol_id": 10, "active": true, "withdrawal_fee_currency_id": 272, "withdrawal_fee_const": 10, "withdrawal_fee_percent": 0, "block_explorer_url": "https://omniexplorer.info/search/" }, // { "protocol_name": "ERC20", "protocol_id": 5, "active": true, "withdrawal_fee_const": 1.2, "withdrawal_fee_percent": 0, "block_explorer_url": "https://etherscan.io/tx/" }, // { "protocol_name": "TRON", "protocol_id": 24, "active": true, "withdrawal_fee_currency_id": 272, "withdrawal_fee_const": 0.2, "withdrawal_fee_percent": 0, "block_explorer_url": "https://tronscan.org/#/transaction/" } // ], // "rates": { "BTC": "0.00013893", "USD": "1" }, // }, // ] // } // const result = { 'info': response }; const balances = this.safeValue (response, 'data', []); for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const code = this.safeCurrencyCode (this.safeString (balance, 'currency_id')); const account = this.account (); account['free'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'frozen_balance'); result[code] = account; } return this.parseBalance (result); } parseOrderStatus (status) { const statuses = { 'PROCESSING': 'open', 'PENDING': 'open', 'PARTIAL': 'open', 'FINISHED': 'closed', 'CANCELLED': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // createOrder, fetchOpenOrders, fetchClosedOrders, cancelOrder, fetchOrder, fetchClosedOrder // // { // "id": 828680665, // "currency_pair_id": 1, // "currency_pair_name": "NXT_BTC", // "price": "0.011384", // "trigger_price": 0.011385, // "initial_amount": "13.942", // "processed_amount": "3.724", // missing in fetchClosedOrder // "type": "SELL", // "original_type": "STOP_LIMIT_SELL", // "created": "2019-01-17 10:14:48", // "timestamp": "1547720088", // "status": "PARTIAL" // // fetchClosedOrder only // "trades": [ // { // "id": 658745, // "buy_order_id": 658745, // "sell_order_id": 828680665, // "price": 0.012285, // "amount": 6.35, // "trade_type": "SELL", // "timestamp": "1538737692" // } // ], // // fetchClosedOrder only // "fees": [ // { // "id": 1234567, // "currency_id": 1, // "amount": 0.00025, // "timestamp": "1548149238" // } // ] // } // const id = this.safeString (order, 'id'); const status = this.parseOrderStatus (this.safeString (order, 'status')); let symbol = undefined; let marketId = this.safeString (order, 'currency_pair_id'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else { marketId = this.safeString (order, 'currency_pair_name'); if (marketId !== undefined) { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.safeTimestamp (order, 'timestamp'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'initial_amount'); const filled = this.safeFloat (order, 'processed_amount'); let remaining = undefined; let cost = undefined; if (filled !== undefined) { if (amount !== undefined) { remaining = amount - filled; if (this.options['parseOrderToPrecision']) { remaining = parseFloat (this.amountToPrecision (symbol, remaining)); } remaining = Math.max (remaining, 0.0); } if (price !== undefined) { if (cost === undefined) { cost = price * filled; } } } let type = this.safeString (order, 'original_type'); if ((type === 'BUY') || (type === 'SELL')) { type = undefined; } const side = this.safeStringLower (order, 'type'); const rawTrades = this.safeValue (order, 'trades'); let trades = undefined; if (rawTrades !== undefined) { trades = this.parseTrades (rawTrades, market, undefined, undefined, { 'symbol': symbol, 'order': id, }); } const result = { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': undefined, 'filled': filled, 'remaining': remaining, 'status': status, 'trades': trades, }; const fees = this.safeValue (order, 'fees'); if (fees === undefined) { result['fee'] = undefined; } else { const numFees = fees.length; if (numFees > 0) { result['fees'] = []; for (let i = 0; i < fees.length; i++) { const feeCost = this.safeFloat (fees[i], 'amount'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (fees[i], 'currency_id'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); result['fees'].push ({ 'cost': feeCost, 'currency': feeCurrencyCode, }); } } } else { result['fee'] = undefined; } } return result; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type === 'market') { throw new ExchangeError (this.id + ' createOrder allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); if (type === 'limit') { type = side; } const request = { 'currencyPairId': market['id'], 'type': type.toUpperCase (), // 'BUY', 'SELL', 'STOP_LIMIT_BUY', 'STOP_LIMIT_SELL' 'amount': parseFloat (this.amountToPrecision (symbol, amount)), // required 'price': parseFloat (this.priceToPrecision (symbol, price)), // required // 'trigger_price': 123.45 // required for STOP_LIMIT_BUY or STOP_LIMIT_SELL }; const response = await this.tradingPostOrdersCurrencyPairId (this.extend (request, params)); // // { // "success": true, // "data": { // "id": 828680665, // "currency_pair_id": 1, // "currency_pair_name": "NXT_BTC", // "price": "0.011384", // "trigger_price": 0.011385, // "initial_amount": "13.942", // "processed_amount": "3.724", // "type": "SELL", // "original_type": "STOP_LIMIT_SELL", // "created": "2019-01-17 10:14:48", // "timestamp": "1547720088", // "status": "PARTIAL" // } // } // const data = this.safeValue (response, 'data', {}); return this.parseOrder (data, market); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; const response = await this.tradingGetOrderOrderId (this.extend (request, params)); // // { // "success": true, // "data": { // "id": 828680665, // "currency_pair_id": 1, // "currency_pair_name": "NXT_BTC", // "price": "0.011384", // "trigger_price": 0.011385, // "initial_amount": "13.942", // "processed_amount": "3.724", // "type": "SELL", // "original_type": "STOP_LIMIT_SELL", // "created": "2019-01-17 10:14:48", // "timestamp": "1547720088", // "status": "PARTIAL" // } // } // const data = this.safeValue (response, 'data', {}); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } return this.parseOrder (data, market); } async fetchClosedOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; const response = await this.reportsGetOrdersOrderId (this.extend (request, params)); // // { // "success": true, // "data": { // "id": 5478965, // "currency_pair_id": 1, // "currency_pair_name": "NXT_BTC", // "price": "0.00013800", // "initial_amount": "1.00000000", // "type": "BUY", // "created": "2019-01-22 09:27:17", // "timestamp": 1548149237, // "status": "FINISHED", // "trades": [ // { // "id": 658745, // "buy_order_id": 6587453, // "sell_order_id": 6587459, // "price": 0.012285, // "amount": 6.35, // "trade_type": "SELL", // "timestamp": "1538737692" // } // ], // "fees": [ // { // "id": 1234567, // "currency_id": 1, // "amount": 0.00025, // "timestamp": "1548149238" // } // ] // } // } // const data = this.safeValue (response, 'data', {}); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } return this.parseOrder (data, market); } async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { const order = await this.fetchClosedOrder (id, symbol, params); return order['trades']; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; let method = 'tradingGetOrders'; const request = { // 'limit': 100, // default 100 // 'offset': 100, }; if (symbol !== undefined) { method = 'tradingGetOrdersCurrencyPairId'; market = this.market (symbol); request['currencyPairId'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } const response = await this[method] (this.extend (request, params)); // // { // "success": true, // "data": [ // { // "id": 828680665, // "currency_pair_id": 1, // "currency_pair_name": "NXT_BTC", // "price": "0.011384", // "trigger_price": 0.011385, // "initial_amount": "13.942", // "processed_amount": "3.724", // "type": "SELL", // "original_type": "STOP_LIMIT_SELL", // "created": "2019-01-17 10:14:48", // "timestamp": "1547720088", // "status": "PARTIAL" // } // ] // } // const data = this.safeValue (response, 'data', []); return this.parseOrders (data, market, since, limit); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderId': id, }; const response = await this.tradingDeleteOrderOrderId (this.extend (request, params)); // // { // "success": true, // "data": { // "put_into_processing_queue": [ // { // "id": 828680665, // "currency_pair_id": 1, // "currency_pair_name": "NXT_BTC", // "price": "0.011384", // "trigger_price": 0.011385, // "initial_amount": "13.942", // "processed_amount": "3.724", // "type": "SELL", // "original_type": "STOP_LIMIT_SELL", // "created": "2019-01-17 10:14:48", // "timestamp": "1547720088", // "status": "PARTIAL" // } // ], // "not_put_into_processing_queue": [ // { // "id": 828680665, // "currency_pair_id": 1, // "currency_pair_name": "NXT_BTC", // "price": "0.011384", // "trigger_price": 0.011385, // "initial_amount": "13.942", // "processed_amount": "3.724", // "type": "SELL", // "original_type": "STOP_LIMIT_SELL", // "created": "2019-01-17 10:14:48", // "timestamp": "1547720088", // "status": "PARTIAL" // } // ], // "message": "string" // } // } // const data = this.safeValue (response, 'data', {}); const acceptedOrders = this.safeValue (data, 'put_into_processing_queue', []); const rejectedOrders = this.safeValue (data, 'not_put_into_processing_queue', []); const numAcceptedOrders = acceptedOrders.length; const numRejectedOrders = rejectedOrders.length; if (numAcceptedOrders < 1) { if (numRejectedOrders < 1) { throw new OrderNotFound (this.id + ' cancelOrder received an empty response: ' + this.json (response)); } else { return this.parseOrder (rejectedOrders[0]); } } else { if (numRejectedOrders < 1) { return this.parseOrder (acceptedOrders[0]); } else { throw new OrderNotFound (this.id + ' cancelOrder received an empty response: ' + this.json (response)); } } } async cancelAllOrders (symbol = undefined, params = {}) { await this.loadMarkets (); const request = {}; let method = 'tradingDeleteOrders'; if (symbol !== undefined) { const market = this.market (symbol); request['currencyPairId'] = market['id']; method = 'tradingDeleteOrdersCurrencyPairId'; } const response = await this[method] (this.extend (request, params)); // // { // "success":true, // "data":{ // "put_into_processing_queue":[], // "not_put_into_processing_queue":[], // "message":"Orders operations are handled in processing queue, therefore cancelling is not immediate." // } // } // return response; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'currencyPairId': market['id'], // 'timeStart': '2019-11-26T19:54:55.901Z', // datetime in iso format // 'timeEnd': '2019-11-26T19:54:55.901Z', // datetime in iso format // 'limit': 100, // default 100 // 'offset': 100, }; if (since !== undefined) { request['timeStart'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.reportsGetTradesCurrencyPairId (this.extend (request, params)); // // { // "success": true, // "data": [ // { // "id": 658745, // "buy_order_id": 6587453, // "sell_order_id": 6587459, // "price": 0.012285, // "amount": 6.35, // "trade_type": "SELL", // "timestamp": "1538737692" // } // ] // } // const trades = this.safeValue (response, 'data', []); return this.parseTrades (trades, market, since, limit); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currencyId': currency['id'], // Default value is the value that represents legacy protocol. // In case of USDT it is 10 as Tether OMNI was the default previously. // The list of protocols can be obtained from the /public/currencies/{currencyId} // 'protocol_id': 10, }; const response = await this.profilePostWalletsCurrencyId (this.extend (request, params)); // // { // "success": true, // "data": { // "id": 45875, // "currency_id": 1, // "delisted": false, // "disabled": false, // "disable_deposits": false, // "code": "BTC", // "balance": "0.198752", // "frozen_balance": "1.5784", // "bonus_balance": "0.000", // "deposit_address": { // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", // "address_name": "Address", // "additional_address_parameter": "qwertyuiopasdfghjkl", // "additional_address_parameter_name": "Destination Tag", // "notification": "", // "protocol_id": 10, // "protocol_name": "Tether OMNI", // "supports_new_address_creation": false // }, // "multi_deposit_addresses": [ // { // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", // "address_name": "Address", // "additional_address_parameter": "qwertyuiopasdfghjkl", // "additional_address_parameter_name": "Destination Tag", // "notification": "", // "protocol_id": 10, // "protocol_name": "Tether OMNI", // "supports_new_address_creation": false // } // ], // "withdrawal_additional_field_name": "Payment ID (optional)", // "rates": { "BTC": 0.000001 }, // "protocol_specific_settings": [ // { // "protocol_name": "Tether OMNI", // "protocol_id": 10, // "active": true, // "withdrawal_fee_currency_id": 1, // "withdrawal_fee_const": 0.002, // "withdrawal_fee_percent": 0, // "block_explorer_url": "https://omniexplorer.info/search/" // } // ] // } // } // const data = this.safeValue (response, 'data', {}); const depositAddress = this.safeValue (data, 'deposit_address', {}); const address = this.safeString (depositAddress, 'address'); const tag = this.safeString (depositAddress, 'additional_address_parameter'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const balance = await this.fetchBalance (); const wallets = this.safeValue (balance['info'], 'data', []); const walletsByCurrencyId = this.indexBy (wallets, 'currency_id'); const currency = this.currency (code); const wallet = this.safeValue (walletsByCurrencyId, currency['id']); if (wallet === undefined) { throw new ExchangeError (this.id + ' fetchDepositAddress() could not find the wallet id for currency code ' + code + ', try to call createDepositAddress() first'); } const walletId = this.safeInteger (wallet, 'id'); if (walletId === undefined) { throw new ExchangeError (this.id + ' fetchDepositAddress() could not find the wallet id for currency code ' + code + ', try to call createDepositAddress() first'); } const request = { 'walletId': walletId, }; const response = await this.profileGetWalletsWalletId (this.extend (request, params)); // // { // "success": true, // "data": { // "id": 45875, // "currency_id": 1, // "delisted": false, // "disabled": false, // "disable_deposits": false, // "code": "BTC", // "balance": "0.198752", // "frozen_balance": "1.5784", // "bonus_balance": "0.000", // "deposit_address": { // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", // "address_name": "Address", // "additional_address_parameter": "qwertyuiopasdfghjkl", // "additional_address_parameter_name": "Destination Tag", // "notification": "", // "protocol_id": 10, // "protocol_name": "Tether OMNI", // "supports_new_address_creation": false // }, // "multi_deposit_addresses": [ // { // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", // "address_name": "Address", // "additional_address_parameter": "qwertyuiopasdfghjkl", // "additional_address_parameter_name": "Destination Tag", // "notification": "", // "protocol_id": 10, // "protocol_name": "Tether OMNI", // "supports_new_address_creation": false // } // ], // "withdrawal_additional_field_name": "Payment ID (optional)", // "rates": { "BTC": 0.000001 }, // "protocol_specific_settings": [ // { // "protocol_name": "Tether OMNI", // "protocol_id": 10, // "active": true, // "withdrawal_fee_currency_id": 1, // "withdrawal_fee_const": 0.002, // "withdrawal_fee_percent": 0, // "block_explorer_url": "https://omniexplorer.info/search/" // } // ] // } // } // const data = this.safeValue (response, 'data', []); const depositAddress = this.safeValue (data, 'deposit_address', {}); const address = this.safeString (depositAddress, 'address'); const tag = this.safeString (depositAddress, 'additional_address_parameter'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + api + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); headers = { 'Authorization': 'Bearer ' + this.token, }; if (method === 'GET' || method === 'DELETE') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { body = this.json (query); if (Object.keys (query).length) { headers['Content-Type'] = 'application/json'; } } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } parseTransactionStatus (status) { const statuses = { 'processing': 'pending', 'checking by system': 'pending', 'hodl': 'pending', 'amount too low': 'failed', 'not confirmed': 'pending', 'cancelled by User': 'canceled', 'approved': 'pending', 'finished': 'ok', 'withdrawal error': 'failed', 'deposit error': 'failed', 'cancelled by admin': 'canceled', 'awaiting': 'pending', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // "id": 123654789, // "currency_id": 1, // "currency_code": "BTC", // "deposit_fee_currency_id": 1, // "deposit_fee_currency_code": "BTC", // "amount": 0.25, // "fee": 0.00025, // "txid": "qwertyuhgfdsasdfgh", // "protocol_id": 0, // "deposit_status_id": 1, // "status": "PROCESSING", // "status_color": "#BC3D51", // "created_at": "2018-11-28 12:32:08", // "timestamp": "1543409389", // "confirmations": "1 of 2" // } // // fetchWithdrawals // // { // "id": 65899, // "amount": "0.00600000", // "currency_id": 1, // "currency_code": "BTC", // "fee": "0.00400000", // "fee_currency_id": 1, // "fee_currency_code": "BTC", // "withdrawal_status_id": 1, // "status": "Not Confirmed", // "status_color": "#BC3D51", // "created_at": "2019-01-21 09:36:05", // "created_ts": "1548063365", // "updated_at": "2019-01-21 09:36:05", // "updated_ts": "1548063365", // "txid": null, // "protocol_id": 0, // "withdrawal_address": { // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", // "address_name": "Address", // "additional_address_parameter": "qwertyuiopasdfghjkl", // "additional_address_parameter_name": "Destination Tag", // "notification": "", // "protocol_id": 10, // "protocol_name": "Tether OMNI", // "supports_new_address_creation": false // } // } // const id = this.safeString (transaction, 'id'); const withdrawalAddress = this.safeValue (transaction, 'withdrawal_address', {}); const address = this.safeString (withdrawalAddress, 'address'); const tag = this.safeString (withdrawalAddress, 'additional_address_parameter'); const currencyId = this.safeString (transaction, 'currency_id'); let code = undefined; if (currencyId in this.currencies_by_id) { currency = this.currencies_by_id[currencyId]; } else { code = this.commonCurrencyCode (this.safeString (transaction, 'currency_code')); } if ((code === undefined) && (currency !== undefined)) { code = currency['code']; } const type = ('deposit_status_id' in transaction) ? 'deposit' : 'withdrawal'; const amount = this.safeFloat (transaction, 'amount'); const status = this.parseTransactionStatus (this.safeStringLower (transaction, 'status')); const timestamp = this.safeTimestamp2 (transaction, 'timestamp', 'created_ts'); const updated = this.safeTimestamp (transaction, 'updated_ts'); const txid = this.safeString (transaction, 'txid'); let fee = undefined; const feeCost = this.safeFloat (transaction, 'fee'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (transaction, 'fee_currency_id', 'deposit_fee_currency_id'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return { 'info': transaction, 'id': id, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'addressFrom': undefined, 'address': address, 'addressTo': address, 'tagFrom': undefined, 'tag': tag, 'tagTo': tag, 'type': type, 'amount': amount, 'currency': code, 'status': status, 'updated': updated, 'fee': fee, }; } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = {}; if (code !== undefined) { currency = this.currency (code); request['currencyId'] = currency['id']; } if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['timeStart'] = since; } const response = await this.profileGetDeposits (this.extend (request, params)); // // { // "success": true, // "data": [ // { // "id": 123654789, // "currency_id": 1, // "currency_code": "BTC", // "deposit_fee_currency_id": 1, // "deposit_fee_currency_code": "BTC", // "amount": 0.25, // "fee": 0.00025, // "txid": "qwertyuhgfdsasdfgh", // "protocol_id": 0, // "deposit_status_id": 1, // "status": "PROCESSING", // "status_color": "#BC3D51", // "created_at": "2018-11-28 12:32:08", // "timestamp": "1543409389", // "confirmations": "1 of 2", // "protocol_specific_settings": { // "protocol_name": "Tether OMNI", // "protocol_id": 10, // "block_explorer_url": "https://omniexplorer.info/search/" // } // } // ] // } // const deposits = this.safeValue (response, 'data', []); return this.parseTransactions (deposits, code, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let currency = undefined; const request = {}; if (code !== undefined) { currency = this.currency (code); request['currencyId'] = currency['id']; } if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['timeStart'] = since; } const response = await this.profileGetWithdrawals (this.extend (request, params)); // // { // "success": true, // "data": [ // { // "id": 65899, // "amount": "0.00600000", // "currency_id": 1, // "currency_code": "BTC", // "fee": "0.00400000", // "fee_currency_id": 1, // "fee_currency_code": "BTC", // "withdrawal_status_id": 1, // "status": "Not Confirmed", // "status_color": "#BC3D51", // "created_at": "2019-01-21 09:36:05", // "created_ts": "1548063365", // "updated_at": "2019-01-21 09:36:05", // "updated_ts": "1548063365", // "txid": null, // "protocol_id": 0, // "withdrawal_address": { // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", // "address_name": "Address", // "additional_address_parameter": "qwertyuiopasdfghjkl", // "additional_address_parameter_name": "Destination Tag", // "notification": "", // "protocol_id": 10, // "protocol_name": "Tether OMNI", // "supports_new_address_creation": false // }, // "protocol_specific_settings": { // "protocol_name": "Tether OMNI", // "protocol_id": 10, // "block_explorer_url": "https://omniexplorer.info/search/" // } // } // ] // } // const withdrawals = this.safeValue (response, 'data', []); return this.parseTransactions (withdrawals, code, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency_id': currency['id'], 'amount': parseFloat (this.currencyToPrecision (code, amount)), 'address': address, // 'protocol_id': 10, // optional, to be used with multicurrency wallets like USDT // 'additional_address_parameter': tag, // optional }; if (tag !== undefined) { request['additional_address_parameter'] = tag; } const response = await this.profilePostWithdraw (this.extend (request, params)); // // { // "success": true, // "data": { // "id": 65899, // "amount": "0.00600000", // "currency_id": 1, // "currency_code": "BTC", // "fee": "0.00400000", // "fee_currency_id": 1, // "fee_currency_code": "BTC", // "withdrawal_status_id": 1, // "status": "Not Confirmed", // "status_color": "#BC3D51", // "created_at": "2019-01-21 09:36:05", // "created_ts": "1548063365", // "updated_at": "2019-01-21 09:36:05", // "updated_ts": "1548063365", // "txid": null, // "protocol_id": 0, // "withdrawal_address": { // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", // "address_name": "Address", // "additional_address_parameter": "qwertyuiopasdfghjkl", // "additional_address_parameter_name": "Destination Tag", // "notification": "", // "protocol_id": 10, // "protocol_name": "Tether OMNI", // "supports_new_address_creation": false // } // } // } // const data = this.safeValue (response, 'data', {}); return this.parseTransaction (data, currency); } async fetchFundingFees (codes = undefined, params = {}) { const response = await this.publicGetCurrencies (params); // // { // "success": true, // "data": [ // { // "id": 1, // "code": "BTC", // "name": "Bitcoin", // "active": true, // "delisted": false, // "precision": 8, // "minimum_tx_confirmations": 24, // "minimum_withdrawal_amount": "0.009", // "minimum_deposit_amount": "0.000003", // "deposit_fee_currency_id": 1, // "deposit_fee_currency_code": "ETH", // "deposit_fee_const": "0.00001", // "deposit_fee_percent": "0", // "withdrawal_fee_currency_id": 1, // "withdrawal_fee_currency_code": "ETH", // "withdrawal_fee_const": "0.0015", // "withdrawal_fee_percent": "0", // "withdrawal_limit": "string", // "block_explorer_url": "https://blockchain.info/tx/", // "protocol_specific_settings": [ // { // "protocol_name": "Tether OMNI", // "protocol_id": 10, // "active": true, // "withdrawal_fee_currency_id": 1, // "withdrawal_fee_const": 0.002, // "withdrawal_fee_percent": 0, // "block_explorer_url": "https://omniexplorer.info/search/" // } // ] // } // ] // } // const data = this.safeValue (response, 'data', []); const withdrawFees = {}; const depositFees = {}; for (let i = 0; i < data.length; i++) { const id = this.safeString (data[i], 'id'); const code = this.safeCurrencyCode (id); withdrawFees[code] = this.safeFloat (data[i], 'withdrawal_fee_const'); depositFees[code] = this.safeFloat (data[i], 'deposit_fee_const'); } return { 'withdraw': withdrawFees, 'deposit': depositFees, 'info': response, }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // // {"success":false,"message":"Wrong parameters","errors":{"candleType":["Invalid Candle Type!"]}} // {"success":false,"message":"Wrong parameters","errors":{"time":["timeStart or timeEnd is less then 1"]}} // {"success":false,"message":"Not enough ETH"} // const success = this.safeValue (response, 'success', false); if (!success) { const message = this.safeString (response, 'message'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],157:[function(require,module,exports){ 'use strict'; // ---------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { InvalidNonce, AuthenticationError, AccountSuspended, InsufficientFunds, ExchangeError, ArgumentsRequired, NotSupported } = require ('./base/errors'); // ---------------------------------------------------------------------------- module.exports = class stronghold extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'stronghold', 'name': 'Stronghold', 'country': [ 'US' ], 'rateLimit': 1000, 'version': 'v1', 'comment': 'This comment is optional', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/52160042-98c1f300-26be-11e9-90dd-da8473944c83.jpg', 'api': { 'public': 'https://api.stronghold.co', 'private': 'https://api.stronghold.co', }, 'www': 'https://stronghold.co', 'doc': [ 'https://docs.stronghold.co', ], }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'password': true, }, 'has': { 'fetchMarkets': true, 'fetchCurrencies': true, 'fetchOrderBook': true, 'fetchOpenOrders': true, 'fetchTime': true, 'fetchTrades': true, 'fetchMyTrades': true, 'fetchDepositAddress': false, 'createDepositAddress': true, 'withdraw': true, 'fetchTicker': false, 'fetchTickers': false, 'fetchAccounts': true, 'fetchTransactions': true, }, 'api': { 'public': { 'get': [ 'utilities/time', 'utilities/uuid', 'venues', 'venues/{venueId}/assets', 'venues/{venueId}/markets', 'venues/{venueId}/markets/{marketId}/orderbook', 'venues/{venueId}/markets/{marketId}/trades', ], 'post': [ 'venues/{venueId}/assets', 'iam/credentials', 'identities', ], 'patch': [ 'identities', ], 'put': [ 'iam/credentials/{credentialId}', ], 'delete': [ 'iam/credentials/{credentialId}', ], }, 'private': { 'get': [ 'venues', 'venues/{venueId}/accounts', 'venues/{venueId}/accounts/{accountId}', 'venues/{venueId}/accounts/{accountId}/payments/{paymentId}', 'venues/{venueId}/accounts/{accountId}/orders', 'venues/{venueId}/accounts/{accountId}/trades', 'venues/{venueId}/accounts/{accountId}/transactions', ], 'post': [ 'venues/{venueId}/accounts', 'venues/{venueId}/accounts/{accountId}/orders', 'venues/{venueId}/accounts/{accountId}/deposit', 'venues/{venueId}/accounts/{accountId}/withdrawal', 'venues/{venueId}/accounts/{accountId}/payments', 'venues/{venueId}/accounts/{accountId}/payments/{paymentId}/stop', 'venues/{venueId}/custody/accounts/{accountId}/operations/{operationId}/signatures', 'venues/{venueId}/anchor/withdrawal', 'venues/{venueId}/testing/friendbot', ], 'delete': [ 'venues/{venueId}/accounts/{accountId}/orders/{orderId}', ], }, }, 'options': { 'accountId': undefined, 'venueId': 'trade-public', 'venues': { 'trade': 'trade-public', 'sandbox': 'sandbox-public', }, 'paymentMethods': { 'ETH': 'ethereum', 'BTC': 'bitcoin', 'XLM': 'stellar', 'XRP': 'ripple', 'LTC': 'litecoin', 'SHX': 'stellar', }, }, 'exceptions': { 'CREDENTIAL_MISSING': AuthenticationError, 'CREDENTIAL_INVALID': AuthenticationError, 'CREDENTIAL_REVOKED': AccountSuspended, 'CREDENTIAL_NO_IDENTITY': AuthenticationError, 'PASSPHRASE_INVALID': AuthenticationError, 'SIGNATURE_INVALID': AuthenticationError, 'TIME_INVALID': InvalidNonce, 'BYPASS_INVALID': AuthenticationError, 'INSUFFICIENT_FUNDS': InsufficientFunds, }, }); } async getActiveAccount () { if (this.options['accountId'] !== undefined) { return this.options['accountId']; } await this.loadAccounts (); const numAccounts = this.accounts.length; if (numAccounts > 0) { return this.accounts[0]['id']; } throw new ExchangeError (this.id + ' requires an accountId.'); } async fetchAccounts (params = {}) { const request = { 'venueId': this.options['venueId'], }; const response = await this.privateGetVenuesVenueIdAccounts (this.extend (request, params)); // // [ { id: '34080200-b25a-483d-a734-255d30ba324d', // venueSpecificId: '' } ... ] // return response['result']; } async fetchTime (params = {}) { const response = await this.publicGetUtilitiesTime (params); // // { // "requestId": "6de8f506-ad9d-4d0d-94f3-ec4d55dfcdb9", // "timestamp": 1536436649207281, // "success": true, // "statusCode": 200, // "result": { // "timestamp": "2018-09-08T19:57:29.207282Z" // } // } // return this.parse8601 (this.safeString (response['result'], 'timestamp')); } async fetchMarkets (params = {}) { const request = { 'venueId': this.options['venueId'], }; const response = await this.publicGetVenuesVenueIdMarkets (this.extend (request, params)); const data = response['result']; // // [ // { // id: 'SHXUSD', // baseAssetId: 'SHX/stronghold.co', // counterAssetId: 'USD/stronghold.co', // minimumOrderSize: '1.0000000', // minimumOrderIncrement: '1.0000000', // minimumPriceIncrement: '0.00010000', // displayDecimalsPrice: 4, // displayDecimalsAmount: 0 // }, // ... // ] // const result = {}; for (let i = 0; i < data.length; i++) { const entry = data[i]; const marketId = entry['id']; const baseId = this.safeString (entry, 'baseAssetId'); const quoteId = this.safeString (entry, 'counterAssetId'); const baseAssetId = baseId.split ('/')[0]; const quoteAssetId = quoteId.split ('/')[0]; const base = this.safeCurrencyCode (baseAssetId); const quote = this.safeCurrencyCode (quoteAssetId); const symbol = base + '/' + quote; const limits = { 'amount': { 'min': this.safeFloat (entry, 'minimumOrderSize'), 'max': undefined, }, }; const precision = { 'price': this.safeInteger (entry, 'displayDecimalsPrice'), 'amount': this.safeInteger (entry, 'displayDecimalsAmount'), }; result[symbol] = { 'symbol': symbol, 'id': marketId, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'info': entry, 'limits': limits, 'active': undefined, }; } return result; } async fetchCurrencies (params = {}) { const request = { 'venueId': this.options['venueId'], }; const response = await this.publicGetVenuesVenueIdAssets (this.extend (request, params)); // // [ // { // id: 'XLM/native', // alias: '', // code: 'XLM', // name: '', // displayDecimalsFull: 7, // displayDecimalsSignificant: 2, // }, // ... // ] // const data = response['result']; const result = {}; const limits = { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }; for (let i = 0; i < data.length; i++) { const entry = data[i]; const assetId = this.safeString (entry, 'id'); const currencyId = this.safeString (entry, 'code'); const code = this.safeCurrencyCode (currencyId); const precision = this.safeInteger (entry, 'displayDecimalsFull'); result[code] = { 'code': code, 'id': assetId, 'precision': precision, 'info': entry, 'active': undefined, 'name': undefined, 'limits': limits, 'fee': undefined, }; } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const marketId = this.marketId (symbol); const request = { 'marketId': marketId, 'venueId': this.options['venueId'], }; const response = await this.publicGetVenuesVenueIdMarketsMarketIdOrderbook (this.extend (request, params)); // // { // marketId: 'ETHBTC', // bids: [ // [ '0.031500', '7.385000' ], // ..., // ], // asks: [ // [ '0.031500', '7.385000' ], // ..., // ], // } // const data = response['result']; const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); return this.parseOrderBook (data, timestamp); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'marketId': market['id'], 'venueId': this.options['venueId'], }; const response = await this.publicGetVenuesVenueIdMarketsMarketIdTrades (this.extend (request, params)); // // { // "requestId": "4d343700-b53f-4975-afcc-732ae9d3c828", // "timestamp": "2018-11-08T19:22:11.399543Z", // "success": true, // "statusCode": 200, // "result": { // "marketId": "", // "trades": [ // [ "0.9", "3.10", "sell", "2018-11-08T19:22:11.399547Z" ], // ... // ], // } // } // return this.parseTrades (response['result']['trades'], market, since, limit); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // [ '0.03177000', '0.0643501', 'sell', '2019-01-27T23:02:04Z' ] // // fetchMyTrades (private) // // { // id: '9cdb109c-d035-47e2-81f8-a0c802c9c5f9', // orderId: 'a38d8bcb-9ff5-4c52-81a0-a40196a66462', // marketId: 'XLMUSD', // side: 'sell', // size: '1.0000000', // price: '0.10440600', // settled: true, // maker: false, // executedAt: '2019-02-01T18:44:21Z' // } // let id = undefined; let takerOrMaker = undefined; let price = undefined; let amount = undefined; let cost = undefined; let side = undefined; let timestamp = undefined; let orderId = undefined; if (Array.isArray (trade)) { price = parseFloat (trade[0]); amount = parseFloat (trade[1]); side = trade[2]; timestamp = this.parse8601 (trade[3]); } else { id = this.safeString (trade, 'id'); price = this.safeFloat (trade, 'price'); amount = this.safeFloat (trade, 'size'); side = this.safeString (trade, 'side'); timestamp = this.parse8601 (this.safeString (trade, 'executedAt')); orderId = this.safeString (trade, 'orderId'); const marketId = this.safeString (trade, 'marketId'); market = this.safeValue (this.markets_by_id, marketId); const isMaker = this.safeValue (trade, 'maker'); takerOrMaker = isMaker ? 'maker' : 'taker'; } if (amount !== undefined && price !== undefined) { cost = amount * price; } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'order': orderId, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'takerOrMaker': takerOrMaker, 'fee': { 'cost': undefined, 'currency': undefined, 'rate': undefined, }, }; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = this.extend ({ 'venueId': this.options['venueId'], 'accountId': await this.getActiveAccount (), }, params); if (!request['accountId']) { throw new ArgumentsRequired (this.id + " fetchTransactions requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); } const response = await this.privateGetVenuesVenueIdAccountsAccountIdTransactions (request); let currency = undefined; if (code !== undefined) { currency = this.currency (code); } return this.parseTransactions (response['result'], currency, since, limit); } parseTransactionStatus (status) { const statuses = { 'queued': 'pending', 'settling': 'pending', }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // { // "id": "6408e003-0f14-4457-9340-ba608992ad5c", // "status": "queued", // "direction": "outgoing", // "amount": "98.95000000", // "assetId": "XLM/native", // "sourceAccount": { // "id": "774fa8ef-600b-4636-b9ed-cd6d23421915", // "venueSpecificId": "GC5FIBIQZTQRMJE34GYF5EKH77GEQ3OHFX3NIP5OKDIZFA6VERLZSHY6" // }, // "destinationAccount": { // "id": "f72b9fb5-9607-4dd3-b31f-6ded21337056", // "venueSpecificId": "GAOWV6CYBE7DEWSWPODXLMI5YB75VXXZJX5OYVQ2YLZH2TVA3TMMSNYW" // } // } const id = this.safeString (transaction, 'id'); const assetId = this.safeString (transaction, 'assetId'); let code = undefined; if (assetId !== undefined) { const currencyId = assetId.split ('/')[0]; code = this.safeCurrencyCode (currencyId); } else { if (currency !== undefined) { code = currency['code']; } } const amount = this.safeFloat (transaction, 'amount'); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const feeCost = this.safeFloat (transaction, 'feeAmount'); let feeRate = undefined; if (feeCost !== undefined) { feeRate = feeCost / amount; } const direction = this.safeString (transaction, 'direction'); const datetime = this.safeString (transaction, 'requestedAt'); const timestamp = this.parse8601 (datetime); const updated = this.parse8601 (this.safeString (transaction, 'updatedAt')); const type = (direction === 'outgoing' || direction === 'withdrawal') ? 'withdrawal' : 'deposit'; const fee = { 'cost': feeCost, 'rate': feeRate, }; return { 'id': id, 'info': transaction, 'currency': code, 'amount': amount, 'status': status, 'fee': fee, 'tag': undefined, 'type': type, 'updated': updated, 'address': undefined, 'txid': undefined, 'timestamp': timestamp, 'datetime': datetime, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = this.extend ({ 'venueId': this.options['venueId'], 'accountId': await this.getActiveAccount (), 'marketID': market['id'], 'type': type, 'side': side, 'size': this.amountToPrecision (symbol, amount), 'price': this.priceToPrecision (symbol, price), }, params); if (!request['accountId']) { throw new ArgumentsRequired (this.id + " createOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); } const response = await this.privatePostVenuesVenueIdAccountsAccountIdOrders (request); return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { const request = this.extend ({ 'venueId': this.options['venueId'], 'accountId': await this.getActiveAccount (), 'orderId': id, }, params); if (!request['accountId']) { throw new ArgumentsRequired (this.id + " cancelOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); } const response = await this.privateDeleteVenuesVenueIdAccountsAccountIdOrdersOrderId (request); return this.parseOrder (response); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } const request = this.extend ({ 'venueId': this.options['venueId'], 'accountId': await this.getActiveAccount (), }, params); if (!request['accountId']) { throw new ArgumentsRequired (this.id + " cancelOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); } const response = await this.privateGetVenuesVenueIdAccountsAccountIdOrders (request); return this.parseOrders (response['result'], market, since, limit); } parseOrder (order, market = undefined) { // { id: '178596', // marketId: 'XLMUSD', // side: 'buy', // size: '1.0000000', // sizeFilled: '0', // price: '0.10000000', // placedAt: '2019-02-01T19:47:52Z' } const marketId = this.safeString (order, 'marketId'); if (marketId !== undefined) { market = this.safeValue (this.marketsById, marketId); } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const id = this.safeString (order, 'id'); const datetime = this.safeString (order, 'placedAt'); const amount = this.safeFloat (order, 'size'); const price = this.safeFloat (order, 'price'); const filled = this.safeFloat (order, 'sizeFilled'); let cost = undefined; let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = amount - filled; } if (price !== undefined) { cost = amount * price; } } return { 'id': id, 'clientOrderId': undefined, 'info': order, 'symbol': symbol, 'datetime': datetime, 'timestamp': this.parse8601 (datetime), 'side': this.safeString (order, 'side'), 'amount': amount, 'filled': filled, 'remaining': remaining, 'price': price, 'cost': cost, 'trades': [], 'lastTradeTimestamp': undefined, 'status': undefined, 'type': undefined, 'average': undefined, 'fee': undefined, }; } nonce () { return this.seconds (); } setSandboxMode (enabled) { if (enabled) { this.options['venueId'] = this.options['venues']['sandbox']; } else { this.options['venueId'] = this.options['venues']['trade']; } } async fetchBalance (params = {}) { const request = this.extend ({ 'venueId': this.options['venueId'], 'accountId': await this.getActiveAccount (), }, params); if (!('accountId' in request)) { throw new ArgumentsRequired (this.id + " fetchBalance requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); } const response = await this.privateGetVenuesVenueIdAccountsAccountId (request); const balances = this.safeValue (response['result'], 'balances'); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const assetId = this.safeString (balance, 'assetId'); if (assetId !== undefined) { const currencyId = assetId.split ('/')[0]; const code = this.safeCurrencyCode (currencyId); const account = {}; account['total'] = this.safeFloat (balance, 'amount'); account['free'] = this.safeFloat (balance, 'availableForTrade'); result[code] = account; } } return this.parseBalance (result); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = this.extend ({ 'venueId': this.options['venueId'], 'accountId': await this.getActiveAccount (), }, params); if (!request['accountId']) { throw new ArgumentsRequired (this.id + " fetchMyTrades requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); } const response = await this.privateGetVenuesVenueIdAccountsAccountIdTrades (request); let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } return this.parseTrades (response['result'], market, since, limit); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const paymentMethod = this.safeString (this.options['paymentMethods'], code); if (paymentMethod === undefined) { throw new NotSupported (this.id + ' createDepositAddress requires code to be BTC, ETH, or XLM'); } const request = this.extend ({ 'venueId': this.options['venueId'], 'accountId': await this.getActiveAccount (), 'assetId': this.currencyId (code), 'paymentMethod': paymentMethod, }, params); if (!request['accountId']) { throw new ArgumentsRequired (this.id + " createDepositAddress requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); } const response = await this.privatePostVenuesVenueIdAccountsAccountIdDeposit (request); // // { // assetId: 'BTC/stronghold.co', // paymentMethod: 'bitcoin', // paymentMethodInstructions: { // deposit_address: 'mzMT9Cfw8JXVWK7rMonrpGfY9tt57ytHt4', // reference: 'sometimes-exists', // }, // direction: 'deposit', // } // const data = response['result']['paymentMethodInstructions']; const address = data['deposit_address']; const tag = this.safeString (data, 'reference'); return { 'currency': code, 'address': this.checkAddress (address), 'tag': tag, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { await this.loadMarkets (); const paymentMethod = this.safeString (this.options['paymentMethods'], code); if (paymentMethod === undefined) { throw new NotSupported (this.id + ' withdraw requires code to be BTC, ETH, or XLM'); } const request = this.extend ({ 'venueId': this.options['venueId'], 'accountId': await this.getActiveAccount (), 'assetId': this.currencyId (code), 'amount': amount, 'paymentMethod': paymentMethod, 'paymentMethodDetails': { 'withdrawal_address': address, }, }, params); if (tag !== undefined) { request['paymentMethodDetails']['reference'] = tag; } if (!request['accountId']) { throw new ArgumentsRequired (this.id + " withdraw requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); } const response = await this.privatePostVenuesVenueIdAccountsAccountIdWithdrawal (request); // // { // "id": "5be48892-1b6e-4431-a3cf-34b38811e82c", // "assetId": "BTC/stronghold.co", // "amount": "10", // "feeAmount": "0.01", // "paymentMethod": "bitcoin", // "paymentMethodDetails": { // "withdrawal_address": "1vHysJeXYV6nqhroBaGi52QWFarbJ1dmQ" // }, // "direction": "withdrawal", // "status": "pending" // } // const data = response['result']; return { 'id': this.safeString (data, 'id'), 'info': response, }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (!response) { return; // fallback to base error handler by default } // // { // requestId: '3e7d17ab-b316-4721-b5aa-f7e6497eeab9', // timestamp: '2019-01-31T21:59:06.696855Z', // success: true, // statusCode: 200, // result: [] // } // const errorCode = this.safeString (response, 'errorCode'); if (errorCode in this.exceptions) { const Exception = this.exceptions[errorCode]; throw new Exception (this.id + ' ' + body); } const success = this.safeValue (response, 'success'); if (!success) { throw new ExchangeError (this.id + ' ' + body); } } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const request = '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'][api] + request; if (Object.keys (query).length) { if (method === 'GET') { url += '?' + this.urlencode (query); } else { body = this.json (query); } } if (api === 'private') { this.checkRequiredCredentials (); const timestamp = this.nonce ().toString (); let payload = timestamp + method + request; if (body !== undefined) { payload += body; } const secret = this.base64ToBinary (this.secret); headers = { 'SH-CRED-ID': this.apiKey, 'SH-CRED-SIG': this.hmac (this.encode (payload), secret, 'sha256', 'base64'), 'SH-CRED-TIME': timestamp, 'SH-CRED-PASS': this.password, 'Content-Type': 'application/json', }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],158:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const foxbit = require ('./foxbit.js'); // --------------------------------------------------------------------------- module.exports = class surbitcoin extends foxbit { describe () { return this.deepExtend (super.describe (), { 'id': 'surbitcoin', 'name': 'SurBitcoin', 'countries': [ 'VE' ], 'has': { 'CORS': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27991511-f0a50194-6481-11e7-99b5-8f02932424cc.jpg', 'api': { 'public': 'https://api.blinktrade.com/api', 'private': 'https://api.blinktrade.com/tapi', }, 'www': 'https://surbitcoin.com', 'doc': 'https://blinktrade.com/docs', }, 'options': { 'brokerId': '1', // https://blinktrade.com/docs/#brokers }, }); } }; },{"./foxbit.js":84}],159:[function(require,module,exports){ 'use strict'; const Exchange = require ('./base/Exchange'); const { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, AuthenticationError, InvalidOrder, OrderNotFound, NotSupported, OrderNotFillable, InvalidAddress, InsufficientFunds } = require ('./base/errors'); module.exports = class theocean extends Exchange { describe () { this.checkRequiredDependencies (); return this.deepExtend (super.describe (), { 'id': 'theocean', 'name': 'The Ocean', 'countries': [ 'US' ], 'rateLimit': 3000, 'version': 'v1', 'requiresWeb3': true, 'timeframes': { '5m': '300', '15m': '900', '1h': '3600', '6h': '21600', '1d': '86400', }, 'has': { 'cancelAllOrders': true, 'CORS': false, // ? 'fetchClosedOrders': true, 'fetchOHLCV': false, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchTickers': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/43103756-d56613ce-8ed7-11e8-924e-68f9d4bcacab.jpg', 'api': 'https://api.theocean.trade', 'www': 'https://theocean.trade', 'doc': 'https://docs.theocean.trade', 'fees': 'https://theocean.trade/fees', }, 'api': { 'public': { 'get': [ 'fee_components', 'token_pairs', 'ticker', 'tickers', 'candlesticks', 'candlesticks/intervals', 'trade_history', 'order_book', 'order/{orderHash}', 'version', ], }, 'private': { 'get': [ 'balance', 'available_balance', 'order_history', 'order/unsigned', 'order/unsigned/market', ], 'post': [ 'order', ], 'delete': [ 'order/{orderHash}', 'order', ], }, }, 'exceptions': { 'exact': { 'Order not found': OrderNotFound, // {"message":"Order not found","errors":...} }, 'broad': { "Price can't exceed 8 digits in precision.": InvalidOrder, // {"message":"Price can't exceed 8 digits in precision.","type":"paramPrice"} 'Order cannot be canceled': InvalidOrder, // {"message":"Order cannot be canceled","type":"General error"} 'Greater than available wallet balance.': InsufficientFunds, 'Fillable amount under minimum': InvalidOrder, // {"message":"Fillable amount under minimum WETH trade size.","type":"paramQuoteTokenAmount"} 'Fillable amount over maximum': InvalidOrder, // {"message":"Fillable amount over maximum TUSD trade size.","type":"paramQuoteTokenAmount"} "Schema validation failed for 'params'": BadRequest, // // {"message":"Schema validation failed for 'params'"} 'Service Temporarily Unavailable': ExchangeNotAvailable, }, }, 'options': { 'decimals': {}, 'fetchOrderMethod': 'fetch_order_from_history', }, }); } async fetchMarkets (params = {}) { const markets = await this.publicGetTokenPairs (params); // // [ // "baseToken": { // "symbol": "ZRX", // "address": "0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570", // "name": "0x Protocol Token", // "decimals": "18", // "minAmount": "10000000000000000000", // "maxAmount": "10000000000000000000000", // "precision": "-8" // }, // "quoteToken": { // "symbol": "ETH", // "address": "0xd0a1e359811322d97991e03f863a0c30c2cf029c", // "name": "Ether Token", // "decimals": "18", // "minAmount": "20000000000000000", // "maxAmount": "20000000000000000000", // "precision": "-8" // } // ] // const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const baseToken = this.safeValue (market, 'baseToken', {}); const quoteToken = this.safeValue (market, 'quoteToken', {}); const baseId = this.safeString (baseToken, 'address'); const quoteId = this.safeString (quoteToken, 'address'); const base = this.safeCurrencyCode (this.safeString (baseToken, 'symbol')); const quote = this.safeCurrencyCode (this.safeString (quoteToken, 'symbol')); const symbol = base + '/' + quote; const id = baseId + '/' + quoteId; const baseDecimals = this.safeInteger (baseToken, 'decimals'); const quoteDecimals = this.safeInteger (quoteToken, 'decimals'); this.options['decimals'][base] = baseDecimals; this.options['decimals'][quote] = quoteDecimals; const precision = { 'amount': -parseInt (baseToken['precision']), 'price': -parseInt (quoteToken['precision']), }; const amountLimits = { 'min': this.fromWei (this.safeString (baseToken, 'minAmount'), baseDecimals), 'max': this.fromWei (this.safeString (baseToken, 'maxAmount'), baseDecimals), }; const priceLimits = { 'min': undefined, 'max': undefined, }; const costLimits = { 'min': this.fromWei (this.safeString (quoteToken, 'minAmount'), quoteDecimals), 'max': this.fromWei (this.safeString (quoteToken, 'maxAmount'), quoteDecimals), }; const limits = { 'amount': amountLimits, 'price': priceLimits, 'cost': costLimits, }; const active = true; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { const baseDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); return [ this.safeTimestamp (ohlcv, 'startTime'), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.fromWei (this.safeString (ohlcv, 'baseVolume'), baseDecimals), ]; } async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { if (since === undefined) { throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'baseTokenAddress': market['baseId'], 'quoteTokenAddress': market['quoteId'], 'interval': this.timeframes[timeframe], }; since = parseInt (since); request['startTime'] = since; const response = await this.publicGetCandlesticks (this.extend (request, params)); // // [ // { // "high": "100.52", // "low": "97.23", // "open": "98.45", // "close": "99.23", // "baseVolume": "2400000000000000000000", // "quoteVolume": "1200000000000000000000", // "startTime": "1512929323784" // }, // { // "high": "100.52", // "low": "97.23", // "open": "98.45", // "close": "99.23", // "volume": "2400000000000000000000", // "startTime": "1512929198980" // } // ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchBalanceByCode (code, params = {}) { if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { throw new InvalidAddress (this.id + ' fetchBalanceByCode() requires the .walletAddress to be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377"'); } await this.loadMarkets (); const currency = this.currency (code); const request = { 'walletAddress': this.walletAddress.toLowerCase (), 'tokenAddress': currency['id'], }; const response = await this.privateGetBalance (this.extend (request, params)); // // {"available":"0","committed":"0","total":"0"} // const decimals = this.safeInteger (this.options['decimals'], code, 18); const free = this.fromWei (this.safeString (response, 'available'), decimals); const used = this.fromWei (this.safeString (response, 'committed'), decimals); const total = this.fromWei (this.safeString (response, 'total'), decimals); return { 'free': free, 'used': used, 'total': total, }; } async fetchBalance (params = {}) { if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { throw new InvalidAddress (this.id + ' fetchBalance() requires the .walletAddress to be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377"'); } let codes = this.safeValue (this.options, 'fetchBalanceCurrencies'); if (codes === undefined) { codes = this.safeValue (params, 'codes'); } if ((codes === undefined) || (!Array.isArray (codes))) { throw new ExchangeError (this.id + ' fetchBalance() requires a `codes` parameter (an array of currency codes)'); } await this.loadMarkets (); const result = {}; for (let i = 0; i < codes.length; i++) { const code = codes[i]; result[code] = await this.fetchBalanceByCode (code); } return this.parseBalance (result); } parseBidAsk (bidask, priceKey = 0, amountKey = 1, market = undefined) { if (market === undefined) { throw new ArgumentsRequired (this.id + ' parseBidAsk requires a market argument'); } const price = parseFloat (bidask[priceKey]); const amountDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); const amount = this.fromWei (bidask[amountKey], 'ether', amountDecimals); return [ price, amount ]; } parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1, market = undefined) { const result = { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'nonce': undefined, }; const sides = [ bidsKey, asksKey ]; for (let i = 0; i < sides.length; i++) { const side = sides[i]; const orders = []; const bidasks = this.safeValue (orderbook, side); for (let k = 0; k < bidasks.length; k++) { orders.push (this.parseBidAsk (bidasks[k], priceKey, amountKey, market)); } result[side] = orders; } result[bidsKey] = this.sortBy (result[bidsKey], 0, true); result[asksKey] = this.sortBy (result[asksKey], 0); return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'baseTokenAddress': market['baseId'], 'quoteTokenAddress': market['quoteId'], }; if (limit !== undefined) { request['depth'] = limit; } const response = await this.publicGetOrderBook (this.extend (request, params)); // // { // "bids": [ // { orderHash: '0xe2b7f80198edb561cc66cd85cb8e5f420073cf1e5143193d8add8774bd8236c4', // price: '30', // availableAmount: '500000000000000000', // creationTimestamp: '1547193525', // expirationTimestampInSec: '1549789124' // } // ], // "asks": [ // { orderHash: '0xe2b7f80198edb561cc66cd85cb8e5f420073cf1e5143193d8add8774bd8236c4', // price: '30', // availableAmount: '500000000000000000', // creationTimestamp: '1547193525', // expirationTimestampInSec: '1549789124' // } // ] // } // return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'availableAmount', market); } parseTicker (ticker, market = undefined) { // // { // "bid": "0.00050915", // "ask": "0.00054134", // "last": "0.00052718", // "volume": "3000000000000000000", // "timestamp": "1512929327792" // } // const timestamp = parseInt (this.safeInteger (ticker, 'timestamp') / 1000); let symbol = undefined; let base = undefined; if (market !== undefined) { symbol = market['symbol']; base = market['base']; } const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); const baseVolume = this.fromWei (this.safeString (ticker, 'volume'), baseDecimals); const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': this.safeFloat (ticker, 'priceChange'), 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const tickers = await this.publicGetTickers (params); // // [{ // "baseTokenAddress": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d", // "quoteTokenAddress": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a", // "ticker": { // "bid": "0.00050915", // "ask": "0.00054134", // "last": "0.00052718", // "volume": "3000000000000000000", // "timestamp": "1512929327792" // } // }] // const result = {}; for (let i = 0; i < tickers.length; i++) { const ticker = tickers[i]; const baseId = this.safeString (ticker, 'baseTokenAddress'); const quoteId = this.safeString (ticker, 'quoteTokenAddress'); const marketId = baseId + '/' + quoteId; let market = undefined; let symbol = marketId; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } result[symbol] = this.parseTicker (ticker['ticker'], market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'baseTokenAddress': market['baseId'], 'quoteTokenAddress': market['quoteId'], }; const response = await this.publicGetTicker (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { // // fetchTrades // // { // "id": "37212", // "transactionHash": "0x5e6e75e1aa681b51b034296f62ac19be7460411a2ad94042dd8ba637e13eac0c", // "amount": "300000000000000000", // "price": "0.00052718", // ------- they also have a "confirmed" status here ↓ ----------------- // "status": "filled", // filled | settled | failed // "lastUpdated": "1520265048996" // } // // parseOrder trades (timeline "actions", "fills") // // { action: "confirmed", // amount: "1000000000000000000", // intentID: "MARKET_INTENT:90jjw2s7gj90jjw2s7gkjjw2s7gl", // txHash: "0x043488fdc3f995bf9e632a32424e41ed126de90f8cb340a1ff006c2a74ca8336", // blockNumber: "8094822", // timestamp: "1532261686" } // let timestamp = this.safeInteger (trade, 'lastUpdated'); if (timestamp !== undefined) { timestamp /= 1000; } const price = this.safeFloat (trade, 'price'); const id = this.safeString (trade, 'id'); const side = this.safeString (trade, 'side'); let symbol = undefined; let base = undefined; if (market !== undefined) { symbol = market['symbol']; base = market['base']; } const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); const amount = this.fromWei (this.safeString (trade, 'amount'), baseDecimals); let cost = undefined; if (amount !== undefined && price !== undefined) { cost = amount * price; } const takerOrMaker = 'taker'; return { 'id': id, 'order': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'baseTokenAddress': market['baseId'], 'quoteTokenAddress': market['quoteId'], }; const response = await this.publicGetTradeHistory (this.extend (request, params)); // // [ // { // "id": "37212", // "transactionHash": "0x5e6e75e1aa681b51b034296f62ac19be7460411a2ad94042dd8ba637e13eac0c", // "amount": "300000000000000000", // "price": "0.00052718", // "status": "filled", // filled | settled | failed // "lastUpdated": "1520265048996" // } // ] // return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { const errorMessage = this.id + ' createOrder() requires `exchange.walletAddress` and `exchange.privateKey`. The .walletAddress should be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377". The .privateKey for that wallet should be a "0x"-prefixed hexstring like "0xe4f40d465efa94c98aec1a51f574329344c772c1bce33be07fa20a56795fdd09".'; if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { throw new InvalidAddress (errorMessage); } if (!this.privateKey || (this.privateKey.indexOf ('0x') !== 0)) { throw new InvalidAddress (errorMessage); } const orderParams = await this.fetchOrderParamsToSign (symbol, type, side, amount, price, params); const unsignedOrder = orderParams['unsignedZeroExOrder']; if (unsignedOrder === undefined) { throw new OrderNotFillable (this.id + ' ' + type + ' order to ' + side + ' ' + symbol + ' is not fillable at the moment'); } const signedOrder = await this.signZeroExOrderV2 (unsignedOrder, this.privateKey); const id = this.safeString (signedOrder, 'orderHash'); await this.postSignedOrder (signedOrder, orderParams, params); const order = await this.fetchOrder (id); order['type'] = type; return order; } async fetchOrderParamsToSign (symbol, type, side, amount, price = undefined, params = {}) { if (side !== 'buy' && side !== 'sell') { throw new ExchangeError (side + ' is not valid side param. Use \'buy\' or \'sell\''); } if (type !== 'market' && type !== 'limit') { throw new ExchangeError (type + ' is not valid type param. Use \'market\' or \'limit\''); } if (type === 'limit' && price === undefined) { throw new ExchangeError ('Price is not provided for limit order'); } await this.loadMarkets (); const market = this.market (symbol); const baseDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); const request = { 'walletAddress': this.walletAddress.toLowerCase (), // Your Wallet Address 'baseTokenAddress': market['baseId'], // Base token address 'quoteTokenAddress': market['quoteId'], // Quote token address 'side': side, // "buy" or "sell" 'amount': this.toWei (this.amountToPrecision (symbol, amount), baseDecimals), // Base token amount in wei }; let method = undefined; if (type === 'limit') { method = 'privateGetOrderUnsigned'; request['price'] = this.priceToPrecision (symbol, price); } else if (type === 'market') { method = 'privateGetOrderUnsignedMarket'; } else { throw new ExchangeError ('Unsupported order type: ' + type); } const response = await this[method] (this.extend (request, params)); return response; } async postSignedOrder (signedOrder, requestParams, params = {}) { let request = requestParams; request['signedZeroExOrder'] = signedOrder; request = this.omit (request, 'unsignedZeroExOrder'); const response = await this.privatePostOrder (this.extend (request, params)); return response; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderHash': id, }; const response = await this.privateDeleteOrderOrderHash (this.extend (request, params)); // // { // "canceledOrder": { // "orderHash": "0x3d6b287c1dc79262d2391ae2ca9d050fdbbab2c8b3180e4a46f9f321a7f1d7a9", // "amount": "100000000000" // } // } // let market = undefined; if (symbol !== undefined) { market = this.market (symbol); } return this.extend (this.parseOrder (response['canceledOrder'], market), { 'status': 'canceled', }); } async cancelAllOrders (symbol = undefined, params = {}) { const response = await this.privateDeleteOrder (params); // // [{ // "canceledOrder": { // "orderHash": "0x3d6b287c1dc79262d2391ae2ca9d050fdbbab2c8b3180e4a46f9f321a7f1d7a9", // "amount": "100000000000" // } // }] // return response; } parseOrder (order, market = undefined) { const zeroExOrder = this.safeValue (order, 'zeroExOrder'); let id = this.safeString (order, 'orderHash'); if ((id === undefined) && (zeroExOrder !== undefined)) { id = this.safeString (zeroExOrder, 'orderHash'); } const side = this.safeString (order, 'side'); const type = this.safeString (order, 'type'); // injected from outside let timestamp = this.safeInteger (order, 'creationTimestamp'); if (timestamp !== 'undefined') { timestamp = parseInt (timestamp / 1000); } let symbol = undefined; const baseId = this.safeString (order, 'baseTokenAddress'); const quoteId = this.safeString (order, 'quoteTokenAddress'); let marketId = undefined; if (baseId !== undefined && quoteId !== undefined) { marketId = baseId + '/' + quoteId; } market = this.safeValue (this.markets_by_id, marketId, market); let base = undefined; if (market !== undefined) { symbol = market['symbol']; base = market['base']; } const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); const price = this.safeFloat (order, 'price'); const filledAmount = this.fromWei (this.safeString (order, 'filledAmount'), baseDecimals); const settledAmount = this.fromWei (this.safeString (order, 'settledAmount'), baseDecimals); const confirmedAmount = this.fromWei (this.safeString (order, 'confirmedAmount'), baseDecimals); const failedAmount = this.fromWei (this.safeString (order, 'failedAmount'), baseDecimals); const deadAmount = this.fromWei (this.safeString (order, 'deadAmount'), baseDecimals); const prunedAmount = this.fromWei (this.safeString (order, 'prunedAmount'), baseDecimals); const amount = this.fromWei (this.safeString (order, 'initialAmount'), baseDecimals); const filled = this.sum (filledAmount, settledAmount, confirmedAmount); let remaining = undefined; let lastTradeTimestamp = undefined; const timeline = this.safeValue (order, 'timeline'); let trades = undefined; let status = undefined; if (timeline !== undefined) { const numEvents = timeline.length; if (numEvents > 0) { const timelineEventsGroupedByAction = this.groupBy (timeline, 'action'); if ('error' in timelineEventsGroupedByAction) { status = 'failed'; } if ('filled' in timelineEventsGroupedByAction) { const fillEvents = this.safeValue (timelineEventsGroupedByAction, 'filled'); const numFillEvents = fillEvents.length; lastTradeTimestamp = this.safeInteger (fillEvents[numFillEvents - 1], 'timestamp'); lastTradeTimestamp = (lastTradeTimestamp !== undefined) ? lastTradeTimestamp : lastTradeTimestamp; trades = []; for (let i = 0; i < numFillEvents; i++) { const trade = this.parseTrade (this.extend (fillEvents[i], { 'price': price, }), market); trades.push (this.extend (trade, { 'order': id, 'type': type, 'side': side, })); } } } } let cost = undefined; if (filled !== undefined) { if (remaining === undefined) { if (amount !== undefined) { remaining = amount - filled; } } if (price !== undefined) { cost = filled * price; } } let fee = undefined; const feeCost = this.safeString (order, 'feeAmount'); if (feeCost !== undefined) { const feeOption = this.safeString (order, 'feeOption'); let feeCurrency = undefined; if (feeOption === 'feeInNative') { if (market !== undefined) { feeCurrency = market['base']; } } else if (feeOption === 'feeInZRX') { feeCurrency = 'ZRX'; } else { throw new NotSupported (this.id + ' encountered an unsupported order fee option: ' + feeOption); } const feeDecimals = this.safeInteger (this.options['decimals'], feeCurrency, 18); fee = { 'cost': this.fromWei (feeCost, feeDecimals), 'currency': feeCurrency, }; } const amountPrecision = market ? market['precision']['amount'] : 8; if (remaining !== undefined) { if (status === undefined) { status = 'open'; const rest = remaining - failedAmount - deadAmount - prunedAmount; if (rest < Math.pow (10, -amountPrecision)) { status = (filled < amount) ? 'canceled' : 'closed'; } } } const result = { 'info': order, 'id': id, 'clientOrderId': undefined, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'remaining': remaining, 'filled': filled, 'status': status, 'fee': fee, 'trades': trades, 'average': undefined, }; return result; } async fetchOpenOrder (id, symbol = undefined, params = {}) { const method = this.options['fetchOrderMethod']; return await this[method] (id, symbol, this.extend ({ 'openAmount': 1, }, params)); } async fetchClosedOrder (id, symbol = undefined, params = {}) { const method = this.options['fetchOrderMethod']; return await this[method] (id, symbol, this.extend (params)); } async fetchOrderFromHistory (id, symbol = undefined, params = {}) { const request = { 'orderHash': id, }; const orders = await this.fetchOrders (symbol, undefined, undefined, this.extend (request, params)); const ordersById = this.indexBy (orders, 'id'); if (id in ordersById) { return ordersById[id]; } throw new OrderNotFound (this.id + ' could not find order ' + id + ' in order history'); } async fetchOrderById (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderHash': id, }; const response = await this.publicGetOrderOrderHash (this.extend (request, params)); // { // baseTokenAddress: '0xb18845c260f680d5b9d84649638813e342e4f8c9', // quoteTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', // side: 'sell', // price: '30', // feeTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', // amount: '500000000000000000', // created: '1547194003', // expires: '1549786003', // zeroExOrder: { // salt: '71810414258284992779348693906799008280152689028521273772736250669496045815907', // maker: '0xfa1a3371bcbfcf3deaa8a6f67784bfbe5b886d7f', // taker: '0x77b18613579d49f252bd237ef113884eb37a7090', // makerFee: '0', // takerFee: '0', // orderHash: '0x368540323af55868dd9ce6ac248e6a91d9b7595252ca061c4ada7612b09af1cf', // feeRecipient: '0x88a64b5e882e5ad851bea5e7a3c8ba7c523fecbe', // makerTokenAmount: '500000000000000000', // takerTokenAmount: '14845250714350000000', // makerTokenAddress: '0xb18845c260f680d5b9d84649638813e342e4f8c9', // takerTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', // exchangeContractAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', // expirationUnixTimestampSec: '1549789602' // }, // feeAmount: '154749285650000000', // feeOption: 'feeInNative', // cancelAfter: '1549786003' // } return this.parseOrder (response); } async fetchOrder (id, symbol = undefined, params = {}) { const request = { 'orderHash': id, }; const orders = await this.fetchOrders (symbol, undefined, undefined, this.extend (request, params)); const numOrders = orders.length; if (numOrders !== 1) { throw new OrderNotFound (this.id + ' order ' + id + ' not found'); } return orders[0]; } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['baseTokenAddress'] = market['baseId']; request['quoteTokenAddress'] = market['quoteId']; } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetOrderHistory (this.extend (request, params)); // // [ // { // "orderHash": "0x94629386298dee69ae63cd3e414336ae153b3f02cffb9ffc53ad71e166615618", // "baseTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d", // "quoteTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990", // "side": "buy", // "openAmount": "10000000000000000000", // "filledAmount": "0", // "reservedAmount": "0", // "settledAmount": "0", // "confirmedAmount": "0", // "deadAmount": "0", // "price": "0.00050915", // "timeline": [ // { // "action": "placed", // "amount": "10000000000000000000", // "timestamp": "1512929327792" // } // ] // } // ] // return this.parseOrders (response, undefined, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'openAmount': 1, // returns open orders with remaining openAmount >= 1 }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'openAmount': 0, // returns closed orders with remaining openAmount === 0 }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'private') { this.checkRequiredCredentials (); const timestamp = this.seconds ().toString (); let prehash = this.apiKey + timestamp + method; if (method === 'POST') { body = this.json (query); prehash += body; } else { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } prehash += this.json ({}); } const signature = this.hmac (this.encode (prehash), this.encode (this.secret), 'sha256', 'base64'); headers = { 'TOX-ACCESS-KEY': this.apiKey, 'TOX-ACCESS-SIGN': signature, 'TOX-ACCESS-TIMESTAMP': timestamp, 'Content-Type': 'application/json', }; } else if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // code 401 and plain body 'Authentication failed' (with single quotes) // this error is sent if you do not submit a proper Content-Type if (body === "'Authentication failed'") { throw new AuthenticationError (this.id + ' ' + body); } const message = this.safeString (response, 'message'); if (message !== undefined) { // // {"message":"Schema validation failed for 'query'","errors":[{"name":"required","argument":"startTime","message":"requires property \"startTime\"","instance":{"baseTokenAddress":"0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570","quoteTokenAddress":"0xd0a1e359811322d97991e03f863a0c30c2cf029c","interval":"300"},"property":"instance"}]} // {"message":"Logic validation failed for 'query'","errors":[{"message":"startTime should be between 0 and current date","type":"startTime"}]} // {"message":"Order not found","errors":[]} // {"message":"Orderbook exhausted for intent MARKET_INTENT:8yjjzd8b0e8yjjzd8b0fjjzd8b0g"} // {"message":"Intent validation failed.","errors":[{"message":"Greater than available wallet balance.","type":"walletBaseTokenAmount"}]} // {"message":"Schema validation failed for 'body'","errors":[{"name":"anyOf","argument":["[subschema 0]","[subschema 1]","[subschema 2]"],"message":"is not any of [subschema 0],[subschema 1],[subschema 2]","instance":{"signedTargetOrder":{"error":{"message":"Unsigned target order validation failed.","errors":[{"message":"Greater than available wallet balance.","type":"walletBaseTokenAmount"}]},"maker":"0x1709c02cd7327d391a39a7671af8a91a1ef8a47b","orderHash":"0xda007ea8b5eca71ac96fe4072f7c1209bb151d898a9cc89bbeaa594f0491ee49","ecSignature":{"v":27,"r":"0xb23ce6c4a7b5d51d77e2d00f6d1d472a3b2e72d5b2be1510cfeb122f9366b79e","s":"0x07d274e6d7a00b65fc3026c2f9019215b1e47a5ac4d1f05e03f90550d27109be"}}},"property":"instance"}]} // {"message":"Schema validation failed for 'params'","errors":[{"name":"pattern","argument":"^0x[0-9a-fA-F]{64}$","message":"does not match pattern \"^0x[0-9a-fA-F]{64}$\"","instance":"1","property":"instance.orderHash"}]} // const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],160:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, BadRequest, OrderNotFound, InvalidAddress } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class therock extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'therock', 'name': 'TheRockTrading', 'countries': [ 'MT' ], 'rateLimit': 1000, 'version': 'v1', 'has': { 'CORS': false, 'fetchTickers': true, 'fetchMyTrades': true, 'fetchLedger': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTransactions': 'emulated', 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchClosedOrders': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766869-75057fa2-5ee9-11e7-9a6f-13e641fa4707.jpg', 'api': 'https://api.therocktrading.com', 'www': 'https://therocktrading.com', 'doc': [ 'https://api.therocktrading.com/doc/v1/index.html', 'https://api.therocktrading.com/doc/', ], }, 'api': { 'public': { 'get': [ 'funds', 'funds/{id}/orderbook', 'funds/{id}/ticker', 'funds/{id}/trades', 'funds/tickers', ], }, 'private': { 'get': [ 'balances', 'balances/{id}', 'discounts', 'discounts/{id}', 'funds', 'funds/{id}', 'funds/{id}/trades', 'funds/{fund_id}/orders', 'funds/{fund_id}/orders/{id}', 'funds/{fund_id}/position_balances', 'funds/{fund_id}/positions', 'funds/{fund_id}/positions/{id}', 'transactions', 'transactions/{id}', 'withdraw_limits/{id}', 'withdraw_limits', ], 'post': [ 'atms/withdraw', 'funds/{fund_id}/orders', ], 'delete': [ 'funds/{fund_id}/orders/{id}', 'funds/{fund_id}/orders/remove_all', ], }, }, 'fees': { 'trading': { 'maker': 0.2 / 100, 'taker': 0.2 / 100, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': { 'BTC': 0.0005, 'BCH': 0.0005, 'PPC': 0.02, 'ETH': 0.001, 'ZEC': 0.001, 'LTC': 0.002, 'EUR': 2.5, // worst-case scenario: https://therocktrading.com/en/pages/fees }, 'deposit': { 'BTC': 0, 'BCH': 0, 'PPC': 0, 'ETH': 0, 'ZEC': 0, 'LTC': 0, 'EUR': 0, }, }, }, 'exceptions': { 'exact': { 'Request already running': BadRequest, 'cannot specify multiple address types': BadRequest, 'Currency is not included in the list': BadRequest, 'Record not found': OrderNotFound, }, 'broad': { 'before must be greater than after param': BadRequest, 'must be shorter than 60 days': BadRequest, 'must be a multiple of (period param) in minutes': BadRequest, 'Address allocation limit reached for currency': InvalidAddress, 'is not a valid value for param currency': BadRequest, ' is invalid': InvalidAddress, }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetFunds (params); // // { funds: [ { id: "BTCEUR", // description: "Trade Bitcoin with Euro", // type: "currency", // base_currency: "EUR", // trade_currency: "BTC", // buy_fee: 0.2, // sell_fee: 0.2, // minimum_price_offer: 0.01, // minimum_quantity_offer: 0.0005, // base_currency_decimals: 2, // trade_currency_decimals: 4, // leverages: [] }, // { id: "LTCEUR", // description: "Trade Litecoin with Euro", // type: "currency", // base_currency: "EUR", // trade_currency: "LTC", // buy_fee: 0.2, // sell_fee: 0.2, // minimum_price_offer: 0.01, // minimum_quantity_offer: 0.01, // base_currency_decimals: 2, // trade_currency_decimals: 2, // leverages: [] } ] } // const markets = this.safeValue (response, 'funds'); const result = []; if (markets === undefined) { throw new ExchangeError (this.id + ' fetchMarkets got an unexpected response'); } else { for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'id'); const baseId = this.safeString (market, 'trade_currency'); const quoteId = this.safeString (market, 'base_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const buy_fee = this.safeFloat (market, 'buy_fee'); const sell_fee = this.safeFloat (market, 'sell_fee'); let taker = Math.max (buy_fee, sell_fee); taker = taker / 100; const maker = taker; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': true, 'maker': maker, 'taker': taker, 'precision': { 'amount': this.safeInteger (market, 'trade_currency_decimals'), 'price': this.safeInteger (market, 'base_currency_decimals'), }, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minimum_quantity_offer'), 'max': undefined, }, 'price': { 'min': this.safeFloat (market, 'minimum_price_offer'), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetBalances (params); const balances = this.safeValue (response, 'balances', []); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'trading_balance'); account['total'] = this.safeFloat (balance, 'balance'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const orderbook = await this.publicGetFundsIdOrderbook (this.extend (request, params)); const timestamp = this.parse8601 (this.safeString (orderbook, 'date')); return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); } parseTicker (ticker, market = undefined) { const timestamp = this.parse8601 (ticker['date']); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': this.safeFloat (ticker, 'close'), // previous day close, if any 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume_traded'), 'quoteVolume': this.safeFloat (ticker, 'volume'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetFundsTickers (params); const tickers = this.indexBy (response['tickers'], 'fund_id'); const ids = Object.keys (tickers); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = this.markets_by_id[id]; const symbol = market['symbol']; const ticker = tickers[id]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const ticker = await this.publicGetFundsIdTicker (this.extend ({ 'id': market['id'], }, params)); return this.parseTicker (ticker, market); } parseTrade (trade, market = undefined) { // // fetchTrades, fetchOrder trades // // { id: 4493548, // fund_id: "ETHBTC", // amount: 0.203, // price: 0.02783576, // side: "buy", // dark: false, // date: "2018-11-30T08:19:18.236Z" } // // fetchMyTrades // // { id: 237338, // fund_id: "BTCEUR", // amount: 0.348, // price: 348, // side: "sell", // dark: false, // order_id: 14920648, // date: "2015-06-03T00:49:49.000Z", // transactions: [ { id: 2770768, // date: "2015-06-03T00:49:49.000Z", // type: "sold_currency_to_fund", // price: 121.1, // currency: "EUR" }, // { id: 2770769, // date: "2015-06-03T00:49:49.000Z", // type: "released_currency_to_fund", // price: 0.348, // currency: "BTC" }, // { id: 2770772, // date: "2015-06-03T00:49:49.000Z", // type: "paid_commission", // price: 0.06, // currency: "EUR", // trade_id: 440492 } ] } // if (!market) { market = this.markets_by_id[trade['fund_id']]; } const timestamp = this.parse8601 (this.safeString (trade, 'date')); const id = this.safeString (trade, 'id'); const orderId = this.safeString (trade, 'order_id'); const side = this.safeString (trade, 'side'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let fee = undefined; let feeCost = undefined; const transactions = this.safeValue (trade, 'transactions', []); const transactionsByType = this.groupBy (transactions, 'type'); const feeTransactions = this.safeValue (transactionsByType, 'paid_commission', []); for (let i = 0; i < feeTransactions.length; i++) { if (feeCost === undefined) { feeCost = 0; } feeCost = this.sum (feeCost, this.safeFloat (feeTransactions[i], 'price')); } if (feeCost !== undefined) { fee = { 'cost': feeCost, 'currency': market['quote'], }; } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } parseLedgerEntryDirection (direction) { const directions = { 'affiliate_earnings': 'in', 'atm_payment': 'in', 'bought_currency_from_fund': 'out', 'bought_shares': 'out', 'paid_commission': 'out', 'paypal_payment': 'in', 'pos_payment': 'in', 'released_currency_to_fund': 'out', 'rollover_commission': 'out', 'sold_currency_to_fund': 'in', 'sold_shares': 'in', 'transfer_received': 'in', 'transfer_sent': 'out', 'withdraw': 'out', // commented types will be shown as-is // 'acquired_currency_from_fund': '', // 'acquired_insurance': '', // 'dividend_distributed_to_holders': '', // 'dividend_from_shares': '', // 'exposed_position': '', // 'insurances_reimbursement': '', // 'lent_currency': '', // 'linden_lab_assessment': '', // 'position_transfer_received': '', // 'return_lent_currency': '', // 'returned_lent_currency': '', // 'the_rock_assessment': '', }; return this.safeString (directions, direction, direction); } parseLedgerEntryType (type) { const types = { 'affiliate_earnings': 'referral', 'atm_payment': 'transaction', 'bought_currency_from_fund': 'trade', 'bought_shares': 'trade', 'paid_commission': 'fee', 'paypal_payment': 'transaction', 'pos_payment': 'transaction', 'released_currency_to_fund': 'trade', 'rollover_commission': 'fee', 'sold_currency_to_fund': 'trade', 'sold_shares': 'trade', 'transfer_received': 'transfer', 'transfer_sent': 'transfer', 'withdraw': 'transaction', // commented types will be shown as-is // 'acquired_currency_from_fund': '', // 'acquired_insurance': '', // 'dividend_distributed_to_holders': '', // 'dividend_from_shares': '', // 'exposed_position': '', // 'insurances_reimbursement': '', // 'lent_currency': '', // 'linden_lab_assessment': '', // 'position_transfer_received': '', // 'return_lent_currency': '', // 'returned_lent_currency': '', // 'the_rock_assessment': '', }; return this.safeString (types, type, type); } parseLedgerEntry (item, currency = undefined) { // // withdrawal // // { // "id": 21311223, // "date": "2015-06-30T13:55:11.000Z", // "type": "withdraw", // "price": 103.00, // "currency": "EUR", // "fund_id": null, // "order_id": null, // "trade_id": null, // "transfer_detail": { // "method": "wire_transfer", // "id": "F112DD3", // "recipient": "IT123456789012", // "confirmations": 0 // } // } // // deposit // // { // "id": 21311222, // "date": "2015-06-30T13:55:11.000Z", // "type": "atm_payment", // "price": 2.01291, // "currency": "BTC", // "fund_id": "null", // "order_id": null, // "trade_id": null, // "transfer_detail": { // "method": "bitcoin", // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", // "confirmations": 3 // } // } // // trade fee // // { // "id": 21311221, // "date": "2015-06-30T13:55:11.000Z", // "type": "paid_commission", // "price": 0.0001, // "fund_id": "BTCEUR", // "order_id": 12832371, // "trade_id": 12923212, // "currency": "BTC", // "transfer_detail": null // } // const id = this.safeString (item, 'id'); let referenceId = undefined; let type = this.safeString (item, 'type'); const direction = this.parseLedgerEntryDirection (type); type = this.parseLedgerEntryType (type); if (type === 'trade' || type === 'fee') { referenceId = this.safeString (item, 'trade_id'); } const currencyId = this.safeString (item, 'currency'); const code = this.safeCurrencyCode (currencyId); const amount = this.safeFloat (item, 'price'); const timestamp = this.parse8601 (this.safeString (item, 'date')); const status = 'ok'; return { 'info': item, 'id': id, 'direction': direction, 'account': undefined, 'referenceId': referenceId, 'referenceAccount': undefined, 'type': type, 'currency': code, 'amount': amount, 'before': undefined, 'after': undefined, 'status': status, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': undefined, }; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'page': 1, // 'fund_id': 'ETHBTC', // filter by fund symbol // 'currency': 'BTC', // filter by currency // 'after': '2015-02-06T08:47:26Z', // filter after a certain timestamp // 'before': '2015-02-06T08:47:26Z', // 'type': 'withdraw', // 'order_id': '12832371', // filter by a specific order ID // 'trade_id': '12923212', // filter by a specific trade ID // 'transfer_method': 'bitcoin', // wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin // 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', // filter by a specific recipient (e.g. Bitcoin address, IBAN) // 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', // filter by a specific transfer ID (e.g. Bitcoin TX hash) }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (since !== undefined) { request['after'] = this.iso8601 (since); } const response = await this.privateGetTransactions (this.extend (request, params)); // // { // "transactions": [ // { // "id": 21311223, // "date": "2015-06-30T13:55:11.000Z", // "type": "withdraw", // "price": 103.00, // "currency": "EUR", // "fund_id": null, // "order_id": null, // "trade_id": null, // "transfer_detail": { // "method": "wire_transfer", // "id": "F112DD3", // "recipient": "IT123456789012", // "confirmations": 0 // } // }, // { // "id": 21311222, // "date": "2015-06-30T13:55:11.000Z", // "type": "atm_payment", // "price": 2.01291, // "currency": "BTC", // "fund_id": "null", // "order_id": null, // "trade_id": null, // "transfer_detail": { // "method": "bitcoin", // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", // "confirmations": 3 // } // }, // { // "id": 21311221, // "date": "2015-06-30T13:55:11.000Z", // "type": "paid_commission", // "price": 0.0001, // "fund_id": "BTCEUR", // "order_id": 12832371, // "trade_id": 12923212, // "currency": "BTC", // "transfer_detail": null // } // ], // "meta": { // "total_count": 1221, // "first": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, // "previous": null, // "current": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, // "next": { "page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2" }, // "last": { "page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221" } // } // } // const transactions = this.safeValue (response, 'transactions', []); return this.parseLedger (transactions, currency, since, limit); } parseTransactionType (type) { const types = { 'withdraw': 'withdrawal', 'atm_payment': 'deposit', }; return this.safeString (types, type, type); } parseTransaction (transaction, currency = undefined) { // // fetchWithdrawals // // // fiat // // { // "id": 21311223, // "date": "2015-06-30T13:55:11.000Z", // "type": "withdraw", // "price": 103.00, // "currency": "EUR", // "fund_id": null, // "order_id": null, // "trade_id": null, // "transfer_detail": { // "method": "wire_transfer", // "id": "F112DD3", // "recipient": "IT123456789012", // "confirmations": 0 // } // } // // { // "id": 12564223, // "date": "2017-08-07T08:13:50.023Z", // "note": "GB7IDL401573388", // "type": "withdraw", // "price": 4345.93, // "fund_id": null, // "currency": "EUR", // "order_id": null, // "trade_id": null, // "transfer_detail": { // "id": "EXECUTEDBUTUNCHECKED", // "method": "wire_transfer", // "recipient": "GB7IDL401573388", // "confirmations": 0 // } // } // // // crypto // // { // id: 20914695, // date: '2018-02-24T07:13:23.002Z', // type: 'withdraw', // price: 2.70883607, // currency: 'BCH', // fund_id: null, // order_id: null, // trade_id: null, // note: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', // transfer_detail: { // method: 'bitcoin_cash', // id: '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', // recipient: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', // confirmations: 0 // } // } // // // fetchDeposits // // // fiat // // { // id: 16176632, // date: '2017-11-20T21:00:13.355Z', // type: 'atm_payment', // price: 5000, // currency: 'EUR', // fund_id: null, // order_id: null, // trade_id: null, // note: 'Mistral deposit', // transfer_detail: { // method: 'wire_transfer', // id: '972JQ49337DX769T', // recipient: null, // confirmations: 0 // } // } // // // crypto // // { // "id": 21311222, // "date": "2015-06-30T13:55:11.000Z", // "type": "atm_payment", // "price": 2.01291, // "currency": "BTC", // "fund_id": "null", // "order_id": null, // "trade_id": null, // "transfer_detail": { // "method": "bitcoin", // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", // "confirmations": 3 // } // } // const id = this.safeString (transaction, 'id'); const type = this.parseTransactionType (this.safeString (transaction, 'type')); const detail = this.safeValue (transaction, 'transfer_detail', {}); const method = this.safeString (detail, 'method'); let txid = undefined; let address = undefined; if (method !== undefined) { if (method !== 'wire_transfer') { txid = this.safeString (detail, 'id'); address = this.safeString (detail, 'recipient'); } } const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId); const amount = this.safeFloat (transaction, 'price'); const timestamp = this.parse8601 (this.safeString (transaction, 'date')); const status = 'ok'; // todo parse tags return { 'info': transaction, 'id': id, 'currency': code, 'amount': amount, 'addressFrom': undefined, 'addressTo': address, 'address': address, 'tagFrom': undefined, 'tagTo': undefined, 'tag': undefined, 'status': status, 'type': type, 'updated': undefined, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': undefined, }; } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'type': 'withdraw', }; return await this.fetchTransactions (code, since, limit, this.extend (request, params)); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'type': 'atm_payment', }; return await this.fetchTransactions (code, since, limit, this.extend (request, params)); } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'page': 1, // 'fund_id': 'ETHBTC', // filter by fund symbol // 'currency': 'BTC', // filter by currency // 'after': '2015-02-06T08:47:26Z', // filter after a certain timestamp // 'before': '2015-02-06T08:47:26Z', // 'type': 'withdraw', // 'order_id': '12832371', // filter by a specific order ID // 'trade_id': '12923212', // filter by a specific trade ID // 'transfer_method': 'bitcoin', // wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin // 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', // filter by a specific recipient (e.g. Bitcoin address, IBAN) // 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', // filter by a specific transfer ID (e.g. Bitcoin TX hash) }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (since !== undefined) { request['after'] = this.iso8601 (since); } params = this.extend (request, params); const response = await this.privateGetTransactions (params); // // { // "transactions": [ // { // "id": 21311223, // "date": "2015-06-30T13:55:11.000Z", // "type": "withdraw", // "price": 103.00, // "currency": "EUR", // "fund_id": null, // "order_id": null, // "trade_id": null, // "transfer_detail": { // "method": "wire_transfer", // "id": "F112DD3", // "recipient": "IT123456789012", // "confirmations": 0 // } // }, // { // "id": 21311222, // "date": "2015-06-30T13:55:11.000Z", // "type": "atm_payment", // "price": 2.01291, // "currency": "BTC", // "fund_id": "null", // "order_id": null, // "trade_id": null, // "transfer_detail": { // "method": "bitcoin", // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", // "confirmations": 3 // } // }, // { // "id": 21311221, // "date": "2015-06-30T13:55:11.000Z", // "type": "paid_commission", // "price": 0.0001, // "fund_id": "BTCEUR", // "order_id": 12832371, // "trade_id": 12923212, // "currency": "BTC", // "transfer_detail": null // } // ], // "meta": { // "total_count": 1221, // "first": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, // "previous": null, // "current": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, // "next": { "page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2" }, // "last": { "page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221" } // } // } // const transactions = this.safeValue (response, 'transactions', []); const transactionTypes = [ 'withdraw', 'atm_payment' ]; const depositsAndWithdrawals = this.filterByArray (transactions, 'type', transactionTypes, false); return this.parseTransactions (depositsAndWithdrawals, currency, since, limit); } parseOrderStatus (status) { const statuses = { 'active': 'open', 'executed': 'closed', 'deleted': 'canceled', // don't know what this status means // 'conditional': '?', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "id": 4325578, // "fund_id":"BTCEUR", // "side":"buy", // "type":"limit", // "status":"executed", // "price":0.0102, // "amount": 50.0, // "amount_unfilled": 0.0, // "conditional_type": null, // "conditional_price": null, // "date":"2015-06-03T00:49:48.000Z", // "close_on": nil, // "leverage": 1.0, // "position_id": null, // "trades": [ // { // "id":237338, // "fund_id":"BTCEUR", // "amount":50, // "price":0.0102, // "side":"buy", // "dark":false, // "date":"2015-06-03T00:49:49.000Z" // } // ] // } // const id = this.safeString (order, 'id'); let symbol = undefined; const marketId = this.safeString (order, 'fund_id'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } const status = this.parseOrderStatus (this.safeString (order, 'status')); const timestamp = this.parse8601 (this.safeString (order, 'date')); const type = this.safeString (order, 'type'); const side = this.safeString (order, 'side'); const amount = this.safeFloat (order, 'amount'); const remaining = this.safeFloat (order, 'amount_unfilled'); let filled = undefined; if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; } } const price = this.safeFloat (order, 'price'); let trades = this.safeValue (order, 'trades'); let cost = undefined; let average = undefined; let lastTradeTimestamp = undefined; if (trades !== undefined) { const numTrades = trades.length; if (numTrades > 0) { trades = this.parseTrades (trades, market, undefined, undefined, { 'orderId': id, }); // todo: determine the cost and the average price from trades cost = 0; filled = 0; for (let i = 0; i < numTrades; i++) { const trade = trades[i]; cost = this.sum (cost, trade['cost']); filled = this.sum (filled, trade['amount']); } if (filled > 0) { average = cost / filled; } lastTradeTimestamp = trades[numTrades - 1]['timestamp']; } else { cost = 0; } } return { 'id': id, 'clientOrderId': undefined, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': filled, 'average': average, 'remaining': remaining, 'fee': undefined, 'trades': trades, }; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': 'active', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const request = { 'status': 'executed', }; return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'fund_id': market['id'], // 'after': '2015-02-06T08:47:26Z', // 'before': '2015-02-06T08:47:26Z' // 'status': 'active', // 'executed', 'conditional' // 'side': 'buy', // 'sell' // 'position_id': 123, // filter orders by margin position id }; if (since !== undefined) { request['after'] = this.iso8601 (since); } const response = await this.privateGetFundsFundIdOrders (this.extend (request, params)); // // { // orders: [ // { // id: 299333648, // fund_id: 'BTCEUR', // side: 'sell', // type: 'limit', // status: 'executed', // price: 5821, // amount: 0.1, // amount_unfilled: 0, // conditional_type: null, // conditional_price: null, // date: '2018-06-18T17:38:16.129Z', // close_on: null, // dark: false, // leverage: 1, // position_id: 0 // } // ] // } // const orders = this.safeValue (response, 'orders', []); return this.parseOrders (orders, market, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': id, 'fund_id': market['id'], }; const response = await this.privatePostFundsFundIdOrdersId (this.extend (request, params)); return this.parseOrder (response); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type === 'market') { price = 0; } const request = { 'fund_id': this.marketId (symbol), 'side': side, 'amount': amount, 'price': price, }; const response = await this.privatePostFundsFundIdOrders (this.extend (request, params)); return this.parseOrder (response); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, 'fund_id': this.marketId (symbol), }; const response = await this.privateDeleteFundsFundIdOrdersId (this.extend (request, params)); return this.parseOrder (response); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; if (limit !== undefined) { request['per_page'] = limit; // default 25 max 200 } if (since !== undefined) { request['after'] = this.iso8601 (since); } const response = await this.privateGetFundsIdTrades (this.extend (request, params)); // // { trades: [ { id: 237338, // fund_id: "BTCEUR", // amount: 0.348, // price: 348, // side: "sell", // dark: false, // order_id: 14920648, // date: "2015-06-03T00:49:49.000Z", // transactions: [ { id: 2770768, // date: "2015-06-03T00:49:49.000Z", // type: "sold_currency_to_fund", // price: 121.1, // currency: "EUR" }, // { id: 2770769, // date: "2015-06-03T00:49:49.000Z", // type: "released_currency_to_fund", // price: 0.348, // currency: "BTC" }, // { id: 2770772, // date: "2015-06-03T00:49:49.000Z", // type: "paid_commission", // price: 0.06, // currency: "EUR", // trade_id: 440492 } ] } ], // meta: { total_count: 31, // first: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" }, // previous: null, // current: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" }, // next: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" }, // last: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" } } } // return this.parseTrades (response['trades'], market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': market['id'], }; if (limit !== undefined) { request['per_page'] = limit; // default 25 max 200 } if (since !== undefined) { request['after'] = this.iso8601 (since); } const response = await this.publicGetFundsIdTrades (this.extend (request, params)); // // { trades: [ { id: 4493548, // fund_id: "ETHBTC", // amount: 0.203, // price: 0.02783576, // side: "buy", // dark: false, // date: "2018-11-30T08:19:18.236Z" }, // { id: 4492926, // fund_id: "ETHBTC", // amount: 0.04, // price: 0.02767034, // side: "buy", // dark: false, // date: "2018-11-30T07:03:03.897Z" } ], // meta: { total_count: null, // first: { page: 1, // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1" }, // previous: null, // current: { page: 1, // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1" }, // next: { page: 2, // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=2" }, // last: null } } // return this.parseTrades (response['trades'], market, since, limit); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); headers = (headers === undefined) ? {} : headers; if (api === 'private') { this.checkRequiredCredentials (); if (Object.keys (query).length) { if (method === 'POST') { body = this.json (query); headers['Content-Type'] = 'application/json'; } else { const queryString = this.rawencode (query); if (queryString.length) { url += '?' + queryString; } } } const nonce = this.nonce ().toString (); const auth = nonce + url; headers['X-TRT-KEY'] = this.apiKey; headers['X-TRT-NONCE'] = nonce; headers['X-TRT-SIGN'] = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); } else if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.rawencode (query); } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // // { // "errors": // [ // { "message": ":currency is not a valid value for param currency","code": "11","meta": { "key":"currency","value":":currency"} }, // { "message": "Address allocation limit reached for currency :currency.","code": "13" }, // { "message": "Request already running", "code": "50"}, // { "message": "cannot specify multiple address types", "code": "12" }, // { "message": ":address_type is invalid", "code": "12" } // ] // } // const errors = this.safeValue (response, 'errors', []); const numErrors = errors.length; if (numErrors > 0) { const feedback = this.id + ' ' + body; // here we throw the first error we can identify for (let i = 0; i < numErrors; i++) { const error = errors[i]; const message = this.safeString (error, 'message'); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); } throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],161:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, InsufficientFunds, OrderNotFound, ArgumentsRequired } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class tidebit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'tidebit', 'name': 'TideBit', 'countries': [ 'HK' ], 'rateLimit': 1000, 'version': 'v2', 'has': { 'fetchDepositAddress': true, 'CORS': false, 'fetchTickers': true, 'fetchOHLCV': true, 'withdraw': true, }, 'timeframes': { '1m': '1', '5m': '5', '15m': '15', '30m': '30', '1h': '60', '2h': '120', '4h': '240', '12h': '720', '1d': '1440', '3d': '4320', '1w': '10080', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/39034921-e3acf016-4480-11e8-9945-a6086a1082fe.jpg', 'api': 'https://www.tidebit.com', 'www': 'https://www.tidebit.com', 'doc': [ 'https://www.tidebit.com/documents/api/guide', 'https://www.tidebit.com/swagger/#/default', ], 'referral': 'http://bit.ly/2IX0LrM', }, 'api': { 'public': { 'get': [ 'markets', 'tickers', 'tickers/{market}', 'timestamp', 'trades', 'trades/{market}', 'order_book', 'order', 'k_with_pending_trades', 'k', 'depth', ], 'post': [], }, 'private': { 'get': [ 'addresses/{address}', 'deposits/history', 'deposits/get_deposit', 'deposits/deposit_address', 'historys/orders', 'historys/vouchers', 'historys/accounts', 'historys/snapshots', 'linkage/get_status', 'members/me', 'order', 'orders', 'partners/orders/{id}/trades', 'referral_commissions/get_undeposited', 'referral_commissions/get_graph_data', 'trades/my', 'withdraws/bind_account_list', 'withdraws/get_withdraw_account', 'withdraws/fetch_bind_info', ], 'post': [ 'deposits/deposit_cash', 'favorite_markets/update', 'order/delete', 'orders', 'orders/multi', 'orders/clear', 'referral_commissions/deposit', 'withdraws/apply', 'withdraws/bind_bank', 'withdraws/bind_address', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.3 / 100, 'taker': 0.3 / 100, }, 'funding': { 'tierBased': false, 'percentage': true, 'withdraw': {}, // There is only 1% fee on withdrawals to your bank account. }, }, 'exceptions': { '2002': InsufficientFunds, '2003': OrderNotFound, }, }); } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privateGetDepositAddress (this.extend (request, params)); if ('success' in response) { if (response['success']) { const address = this.safeString (response, 'address'); const tag = this.safeString (response, 'addressTag'); return { 'currency': code, 'address': this.checkAddress (address), 'tag': tag, 'info': response, }; } } } async fetchMarkets (params = {}) { const response = await this.publicGetMarkets (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'id'); const symbol = this.safeString (market, 'name'); const [ baseId, quoteId ] = symbol.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': undefined, 'precision': this.precision, 'limits': this.limits, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetMembersMe (params); const balances = this.safeValue (response, 'accounts'); const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'locked'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = 300 } request['market'] = market['id']; const response = await this.publicGetDepth (this.extend (request, params)); const timestamp = this.safeTimestamp (response, 'timestamp'); return this.parseOrderBook (response, timestamp); } parseTicker (ticker, market = undefined) { const timestamp = this.safeTimestamp (ticker, 'at'); ticker = this.safeValue (ticker, 'ticker', {}); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'ask': this.safeFloat (ticker, 'sell'), 'bidVolume': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'change': undefined, 'percentage': undefined, 'previousClose': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const tickers = await this.publicGetTickers (params); const ids = Object.keys (tickers); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; let market = undefined; let symbol = id; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } else { const baseId = id.slice (0, 3); const quoteId = id.slice (3, 6); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } const ticker = tickers[id]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTickersMarket (this.extend (request, params)); return this.parseTicker (response, market); } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); const id = this.safeString (trade, 'id'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'volume'); const cost = this.safeFloat (trade, 'funds'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': undefined, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, ohlcv[1], ohlcv[2], ohlcv[3], ohlcv[4], ohlcv[5], ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 30; // default is 30 } const request = { 'market': market['id'], 'period': this.timeframes[timeframe], 'limit': limit, }; if (since !== undefined) { request['timestamp'] = parseInt (since / 1000); } else { request['timestamp'] = 1800000; } const response = await this.publicGetK (this.extend (request, params)); if (response === 'null') { return []; } return this.parseOHLCVs (response, market, timeframe, since, limit); } parseOrderStatus (status) { const statuses = { 'done': 'closed', 'wait': 'open', 'cancel': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "id": 7, // 唯一的 Order ID // "side": "sell", // Buy/Sell 代表买单/卖单 // "price": "3100.0", // 出价 // "avg_price": "3101.2", // 平均成交价 // "state": "wait", // 订单的当前状态 [wait,done,cancel] // // wait 表明订单正在市场上挂单 // // 是一个active order // // 此时订单可能部分成交或者尚未成交 // // done 代表订单已经完全成交 // // cancel 代表订单已经被撤销 // "market": "btccny", // 订单参与的交易市场 // "created_at": "2014-04-18T02:02:33Z", // 下单时间 ISO8601格式 // "volume": "100.0", // 购买/卖出数量 // "remaining_volume": "89.8", // 还未成交的数量 remaining_volume 总是小于等于 volume // // 在订单完全成交时变成 0 // "executed_volume": "10.2", // 已成交的数量 // // volume = remaining_volume + executed_volume // "trades_count": 1, // 订单的成交数 整数值 // // 未成交的订单为 0 有一笔成交的订单为 1 // // 通过该字段可以判断订单是否处于部分成交状态 // "trades": [ // 订单的详细成交记录 参见Trade // // 注意: 只有某些返回详细订单数据的 API 才会包含 Trade 数据 // { // "id": 2, // "price": "3100.0", // "volume": "10.2", // "market": "btccny", // "created_at": "2014-04-18T02:04:49Z", // "side": "sell" // } // ] // } // let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } else { const marketId = order['market']; symbol = this.markets_by_id[marketId]['symbol']; } const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const status = this.parseOrderStatus (this.safeString (order, 'state')); const id = this.safeString (order, 'id'); const type = this.safeString (order, 'ord_type'); const side = this.safeString (order, 'side'); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'volume'); const filled = this.safeFloat (order, 'executed_volume'); const remaining = this.safeFloat (order, 'remaining_volume'); let cost = undefined; if (price !== undefined) { if (filled !== undefined) { cost = price * filled; } } return { 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'filled': filled, 'remaining': remaining, 'cost': cost, 'trades': undefined, 'fee': undefined, 'info': order, 'average': undefined, }; } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const request = { 'market': this.marketId (symbol), 'side': side, 'volume': amount.toString (), 'ord_type': type, }; if (type === 'limit') { request['price'] = price.toString (); } const response = await this.privatePostOrders (this.extend (request, params)); const market = this.markets_by_id[response['market']]; return this.parseOrder (response, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; const result = await this.privatePostOrderDelete (this.extend (request, params)); const order = this.parseOrder (result); const status = this.safeString (order, 'status'); if (status === 'closed' || status === 'canceled') { throw new OrderNotFound (this.id + ' ' + this.json (order)); } return order; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const id = this.safeString (params, 'id'); if (id === undefined) { throw new ArgumentsRequired (this.id + ' withdraw() requires an extra `id` param (withdraw account id according to withdraws/bind_account_list endpoint'); } const request = { 'id': id, 'currency_type': 'coin', // or 'cash' 'currency': currency['id'], 'body': amount, // 'address': address, // they don't allow withdrawing to direct addresses? }; if (tag !== undefined) { request['memo'] = tag; } const result = await this.privatePostWithdrawsApply (this.extend (request, params)); return { 'info': result, 'id': undefined, }; } nonce () { return this.milliseconds (); } encodeParams (params) { return this.urlencode (this.keysort (params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const request = '/' + 'api/' + this.version + '/' + this.implodeParams (path, params) + '.json'; const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'] + request; if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); const nonce = this.nonce ().toString (); const sortedByKey = this.keysort (this.extend ({ 'access_key': this.apiKey, 'tonce': nonce, }, params)); const query = this.urlencode (sortedByKey); const payload = method + '|' + request + '|' + query; const signature = this.hmac (this.encode (payload), this.encode (this.secret)); const suffix = query + '&signature=' + signature; if (method === 'GET') { url += '?' + suffix; } else { body = suffix; headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if ((code === 400) || (response === undefined)) { const feedback = this.id + ' ' + body; if (response === undefined) { throw new ExchangeError (feedback); } const error = this.safeValue (response, 'error', {}); const errorCode = this.safeString (error, 'code'); this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); // fallback to default error handler } } }; },{"./base/Exchange":7,"./base/errors":9}],162:[function(require,module,exports){ 'use strict'; const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); module.exports = class tidex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'tidex', 'name': 'Tidex', 'countries': [ 'UK' ], 'rateLimit': 2000, 'version': '3', 'userAgent': this.userAgents['chrome'], 'has': { 'CORS': false, 'createMarketOrder': false, 'fetchOrderBooks': true, 'fetchOrder': true, 'fetchOrders': 'emulated', 'fetchOpenOrders': true, 'fetchClosedOrders': 'emulated', 'fetchTickers': true, 'fetchMyTrades': true, 'withdraw': true, 'fetchCurrencies': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/30781780-03149dc4-a12e-11e7-82bb-313b269d24d4.jpg', 'api': { 'web': 'https://gate.tidex.com/api', 'public': 'https://api.tidex.com/api/3', 'private': 'https://api.tidex.com/tapi', }, 'www': 'https://tidex.com', 'doc': 'https://tidex.com/exchange/public-api', 'referral': 'https://tidex.com/exchange/?ref=57f5638d9cd7', 'fees': [ 'https://tidex.com/exchange/assets-spec', 'https://tidex.com/exchange/pairs-spec', ], }, 'api': { 'web': { 'get': [ 'currency', 'pairs', 'tickers', 'orders', 'ordershistory', 'trade-data', 'trade-data/{id}', ], }, 'public': { 'get': [ 'info', 'ticker/{pair}', 'depth/{pair}', 'trades/{pair}', ], }, 'private': { 'post': [ 'getInfoExt', 'getInfo', 'Trade', 'ActiveOrders', 'OrderInfo', 'CancelOrder', 'TradeHistory', 'CoinDepositAddress', 'WithdrawCoin', 'CreateCoupon', 'RedeemCoupon', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.1 / 100, 'maker': 0.1 / 100, }, }, 'commonCurrencies': { 'DSH': 'DASH', 'EMGO': 'MGO', 'MGO': 'WMGO', }, 'exceptions': { 'exact': { '803': InvalidOrder, // "Count could not be less than 0.001." (selling below minAmount) '804': InvalidOrder, // "Count could not be more than 10000." (buying above maxAmount) '805': InvalidOrder, // "price could not be less than X." (minPrice violation on buy & sell) '806': InvalidOrder, // "price could not be more than X." (maxPrice violation on buy & sell) '807': InvalidOrder, // "cost could not be less than X." (minCost violation on buy & sell) '831': InsufficientFunds, // "Not enougth X to create buy order." (buying with balance.quote < order.cost) '832': InsufficientFunds, // "Not enougth X to create sell order." (selling with balance.base < order.amount) '833': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) }, 'broad': { 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} 'invalid api key': AuthenticationError, 'invalid sign': AuthenticationError, 'api key dont have trade permission': AuthenticationError, 'invalid parameter': InvalidOrder, 'invalid order': InvalidOrder, 'Requests too often': DDoSProtection, 'not available': ExchangeNotAvailable, 'data unavailable': ExchangeNotAvailable, 'external service unavailable': ExchangeNotAvailable, }, }, 'options': { 'fetchTickersMaxLength': 2048, }, }); } async fetchCurrencies (params = {}) { const response = await this.webGetCurrency (params); const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'symbol'); const precision = currency['amountPoint']; const code = this.safeCurrencyCode (id); let active = currency['visible'] === true; const canWithdraw = currency['withdrawEnable'] === true; const canDeposit = currency['depositEnable'] === true; if (!canWithdraw || !canDeposit) { active = false; } const name = this.safeString (currency, 'name'); result[code] = { 'id': id, 'code': code, 'name': name, 'active': active, 'precision': precision, 'funding': { 'withdraw': { 'active': canWithdraw, 'fee': currency['withdrawFee'], }, 'deposit': { 'active': canDeposit, 'fee': 0.0, }, }, 'limits': { 'amount': { 'min': undefined, 'max': Math.pow (10, precision), }, 'price': { 'min': Math.pow (10, -precision), 'max': Math.pow (10, precision), }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'withdrawMinAmount'), 'max': undefined, }, 'deposit': { 'min': this.safeFloat (currency, 'depositMinAmount'), 'max': undefined, }, }, 'info': currency, }; } return result; } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; let key = 'quote'; const rate = market[takerOrMaker]; let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); if (side === 'sell') { cost *= price; } else { key = 'base'; } return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': cost, }; } async fetchMarkets (params = {}) { const response = await this.publicGetInfo (params); const markets = response['pairs']; const keys = Object.keys (markets); const result = []; for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = markets[id]; const [ baseId, quoteId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'decimal_places'), 'price': this.safeInteger (market, 'decimal_places'), }; const limits = { 'amount': { 'min': this.safeFloat (market, 'min_amount'), 'max': this.safeFloat (market, 'max_amount'), }, 'price': { 'min': this.safeFloat (market, 'min_price'), 'max': this.safeFloat (market, 'max_price'), }, 'cost': { 'min': this.safeFloat (market, 'min_total'), }, }; const hidden = this.safeInteger (market, 'hidden'); const active = (hidden === 0); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'taker': market['fee'] / 100, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGetInfoExt (params); const balances = this.safeValue (response, 'return'); const result = { 'info': balances }; const funds = this.safeValue (balances, 'funds', {}); const currencyIds = Object.keys (funds); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const balance = this.safeValue (funds, currencyId, {}); const account = this.account (); account['free'] = this.safeFloat (balance, 'value'); account['used'] = this.safeFloat (balance, 'inOrders'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = 150, max = 2000 } const response = await this.publicGetDepthPair (this.extend (request, params)); const market_id_in_reponse = (market['id'] in response); if (!market_id_in_reponse) { throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); } const orderbook = response[market['id']]; return this.parseOrderBook (orderbook); } async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let ids = undefined; if (symbols === undefined) { ids = this.ids.join ('-'); // max URL length is 2083 symbols, including http schema, hostname, tld, etc... if (ids.length > 2048) { const numIds = this.ids.length; throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); } } else { ids = this.marketIds (symbols); ids = ids.join ('-'); } const request = { 'pair': ids, }; if (limit !== undefined) { request['limit'] = limit; // default = 150, max = 2000 } const response = await this.publicGetDepthPair (this.extend (request, params)); const result = {}; ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; let symbol = id; if (id in this.markets_by_id) { symbol = this.markets_by_id[id]['symbol']; } result[symbol] = this.parseOrderBook (response[id]); } return result; } parseTicker (ticker, market = undefined) { // // { high: 0.03497582, // low: 0.03248474, // avg: 0.03373028, // vol: 120.11485715062999, // vol_cur: 3572.24914074, // last: 0.0337611, // buy: 0.0337442, // sell: 0.03377798, // updated: 1537522009 } // let timestamp = this.safeTimestamp (ticker, 'updated'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; if (!market['active']) { timestamp = undefined; } } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker, 'avg'), 'baseVolume': this.safeFloat (ticker, 'vol_cur'), 'quoteVolume': this.safeFloat (ticker, 'vol'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); let ids = this.ids; if (symbols === undefined) { const numIds = ids.length; ids = ids.join ('-'); // max URL length is 2048 symbols, including http schema, hostname, tld, etc... if (ids.length > this.options['fetchTickersMaxLength']) { const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); } } else { ids = this.marketIds (symbols); ids = ids.join ('-'); } const request = { 'pair': ids, }; const response = await this.publicGetTickerPair (this.extend (request, params)); const result = {}; const keys = Object.keys (response); for (let i = 0; i < keys.length; i++) { const id = keys[i]; let symbol = id; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } result[symbol] = this.parseTicker (response[id], market); } return result; } async fetchTicker (symbol, params = {}) { const tickers = await this.fetchTickers ([ symbol ], params); return tickers[symbol]; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'timestamp'); let side = this.safeString (trade, 'type'); if (side === 'ask') { side = 'sell'; } else if (side === 'bid') { side = 'buy'; } const price = this.safeFloat2 (trade, 'rate', 'price'); const id = this.safeString2 (trade, 'trade_id', 'tid'); const orderId = this.safeString (trade, 'order_id'); if ('pair' in trade) { const marketId = this.safeString (trade, 'pair'); market = this.safeValue (this.markets_by_id, marketId, market); } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const amount = this.safeFloat (trade, 'amount'); const type = 'limit'; // all trades are still limit trades let takerOrMaker = undefined; let fee = undefined; const feeCost = this.safeFloat (trade, 'commission'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } const isYourOrder = this.safeValue (trade, 'is_your_order'); if (isYourOrder !== undefined) { takerOrMaker = 'taker'; if (isYourOrder) { takerOrMaker = 'maker'; } if (fee === undefined) { fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); } } let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } return { 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetTradesPair (this.extend (request, params)); if (Array.isArray (response)) { const numElements = response.length; if (numElements === 0) { return []; } } return this.parseTrades (response[market['id']], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'type': side, 'amount': this.amountToPrecision (symbol, amount), 'rate': this.priceToPrecision (symbol, price), }; price = parseFloat (price); amount = parseFloat (amount); const response = await this.privatePostTrade (this.extend (request, params)); let id = undefined; let status = 'open'; let filled = 0.0; let remaining = amount; if ('return' in response) { id = this.safeString (response['return'], 'order_id'); if (id === '0') { id = this.safeString (response['return'], 'init_order_id'); status = 'closed'; } filled = this.safeFloat (response['return'], 'received', 0.0); remaining = this.safeFloat (response['return'], 'remains', amount); } const timestamp = this.milliseconds (); const order = { 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': price * filled, 'amount': amount, 'remaining': remaining, 'filled': filled, 'fee': undefined, // 'trades': this.parseTrades (order['trades'], market), 'info': response, 'clientOrderId': undefined, 'average': undefined, 'trades': undefined, }; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': parseInt (id), }; const response = await this.privatePostCancelOrder (this.extend (request, params)); if (id in this.orders) { this.orders[id]['status'] = 'canceled'; } return response; } parseOrderStatus (status) { const statuses = { '0': 'open', '1': 'closed', '2': 'canceled', '3': 'canceled', // or partially-filled and still open? https://github.com/ccxt/ccxt/issues/1594 }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const id = this.safeString (order, 'id'); const status = this.parseOrderStatus (this.safeString (order, 'status')); const timestamp = this.safeTimestamp (order, 'timestamp_created'); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'pair'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } let remaining = undefined; let amount = undefined; const price = this.safeFloat (order, 'rate'); let filled = undefined; let cost = undefined; if ('start_amount' in order) { amount = this.safeFloat (order, 'start_amount'); remaining = this.safeFloat (order, 'amount'); } else { remaining = this.safeFloat (order, 'amount'); if (id in this.orders) { amount = this.orders[id]['amount']; } } if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; cost = price * filled; } } const fee = undefined; return { 'info': order, 'id': id, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'type': 'limit', 'side': order['type'], 'price': price, 'cost': cost, 'amount': amount, 'remaining': remaining, 'filled': filled, 'status': status, 'fee': fee, 'clientOrderId': undefined, 'average': undefined, 'trades': undefined, }; } parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { const result = []; const ids = Object.keys (orders); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } for (let i = 0; i < ids.length; i++) { const id = ids[i]; const order = this.extend ({ 'id': id }, orders[id]); result.push (this.extend (this.parseOrder (order, market), params)); } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': parseInt (id), }; const response = await this.privatePostOrderInfo (this.extend (request, params)); id = id.toString (); const newOrder = this.parseOrder (this.extend ({ 'id': id }, response['return'][id])); const oldOrder = (id in this.orders) ? this.orders[id] : {}; this.orders[id] = this.extend (oldOrder, newOrder); return this.orders[id]; } updateCachedOrders (openOrders, symbol) { // update local cache with open orders // this will add unseen orders and overwrite existing ones for (let j = 0; j < openOrders.length; j++) { const id = openOrders[j]['id']; this.orders[id] = openOrders[j]; } const openOrdersIndexedById = this.indexBy (openOrders, 'id'); const cachedOrderIds = Object.keys (this.orders); for (let k = 0; k < cachedOrderIds.length; k++) { // match each cached order to an order in the open orders array // possible reasons why a cached order may be missing in the open orders array: // - order was closed or canceled -> update cache // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip const cachedOrderId = cachedOrderIds[k]; let cachedOrder = this.orders[cachedOrderId]; if (!(cachedOrderId in openOrdersIndexedById)) { // cached order is not in open orders array // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order if (symbol !== undefined && symbol !== cachedOrder['symbol']) { continue; } // cached order is absent from the list of open orders -> mark the cached order as closed if (cachedOrder['status'] === 'open') { cachedOrder = this.extend (cachedOrder, { 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") 'cost': undefined, 'filled': cachedOrder['amount'], 'remaining': 0.0, }); if (cachedOrder['cost'] === undefined) { if (cachedOrder['filled'] !== undefined) { cachedOrder['cost'] = cachedOrder['filled'] * cachedOrder['price']; } } this.orders[cachedOrderId] = cachedOrder; } } } return this.toArray (this.orders); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if ('fetchOrdersRequiresSymbol' in this.options) { if (this.options['fetchOrdersRequiresSymbol']) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders requires a `symbol` argument'); } } } await this.loadMarkets (); const request = {}; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } const response = await this.privatePostActiveOrders (this.extend (request, params)); // it can only return 'open' orders (i.e. no way to fetch 'closed' orders) const orders = this.safeValue (response, 'return', []); const openOrders = this.parseOrders (orders, market); const allOrders = this.updateCachedOrders (openOrders, symbol); const result = this.filterBySymbol (allOrders, symbol); return this.filterBySinceLimit (result, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'open'); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'closed'); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; // some derived classes use camelcase notation for request fields const request = { // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) // 'count': 1000, // the number of trades for display numerical, default = 1000 // 'from_id': trade ID, from which the display starts numerical 0 // 'end_id': trade ID on which the display ends numerical ∞ // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) // 'pair': 'eth_btc', // default = all markets }; if (symbol !== undefined) { market = this.market (symbol); request['pair'] = market['id']; } if (limit !== undefined) { request['count'] = parseInt (limit); } if (since !== undefined) { request['since'] = parseInt (since / 1000); } const response = await this.privatePostTradeHistory (this.extend (request, params)); const trades = this.safeValue (response, 'return', []); return this.parseTrades (trades, market, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'coinName': currency['id'], 'amount': parseFloat (amount), 'address': address, }; // no docs on the tag, yet... if (tag !== undefined) { throw new ExchangeError (this.id + ' withdraw() does not support the tag argument yet due to a lack of docs on withdrawing with tag/memo on behalf of the exchange.'); } const response = await this.privatePostWithdrawCoin (this.extend (request, params)); return { 'info': response, 'id': response['return']['tId'], }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; const query = this.omit (params, this.extractParams (path)); if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'nonce': nonce, 'method': path, }, query)); const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': signature, }; } else if (api === 'public') { url += '/' + this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { url += '/' + this.implodeParams (path, params); if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { if (Object.keys (query).length) { body = this.json (query); headers = { 'Content-Type': 'application/json', }; } } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('success' in response) { // // 1 - The exchange only returns the integer 'success' key from their private API // // { "success": 1, ... } httpCode === 200 // { "success": 0, ... } httpCode === 200 // // 2 - However, derived exchanges can return non-integers // // It can be a numeric string // { "sucesss": "1", ... } // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) // // Or just a string // { "success": "true", ... } // { "success": "false", ... }, httpCode >= 200 // // Or a boolean // { "success": true, ... } // { "success": false, ... }, httpCode >= 200 // // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types // // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui // // To cover points 1, 2, 3 and 4 combined this handler should work like this: // let success = this.safeValue (response, 'success', false); if (typeof success === 'string') { if ((success === 'true') || (success === '1')) { success = true; } else { success = false; } } if (!success) { const code = this.safeString (response, 'code'); const message = this.safeString (response, 'error'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } } }; },{"./base/Exchange":7,"./base/errors":9}],163:[function(require,module,exports){ 'use strict'; const Exchange = require ('./base/Exchange'); const { ExchangeError, PermissionDenied, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, RateLimitExceeded, NotSupported, BadRequest, AuthenticationError } = require ('./base/errors'); module.exports = class timex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'timex', 'name': 'TimeX', 'countries': [ 'AU' ], 'version': 'v1', 'rateLimit': 1500, 'has': { 'CORS': false, 'cancelOrders': true, 'editOrder': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchTickers': true, 'fetchTradingFee': true, // maker fee only }, 'timeframes': { '1m': 'I1', '5m': 'I5', '15m': 'I15', '30m': 'I30', '1h': 'H1', '2h': 'H2', '4h': 'H4', '6h': 'H6', '12h': 'H12', '1d': 'D1', '1w': 'W1', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/70423869-6839ab00-1a7f-11ea-8f94-13ae72c31115.jpg', 'api': 'https://plasma-relay-backend.timex.io', 'www': 'https://timex.io', 'doc': 'https://docs.timex.io', 'referral': 'https://timex.io/?refcode=1x27vNkTbP1uwkCck', }, 'api': { 'custody': { 'get': [ 'credentials', // Get api key for address 'credentials/h/{hash}', // Get api key by hash 'credentials/k/{key}', // Get api key by key 'credentials/me/address', // Get api key by hash 'deposit-addresses', // Get deposit addresses list 'deposit-addresses/h/{hash}', // Get deposit address by hash ], }, 'history': { 'get': [ 'orders', // Gets historical orders 'orders/details', // Gets order details 'orders/export/csv', // Export orders to csv 'trades', // Gets historical trades 'trades/export/csv', // Export trades to csv ], }, 'currencies': { 'get': [ 'a/{address}', // Gets currency by address 'i/{id}', // Gets currency by id 's/{symbol}', // Gets currency by symbol ], 'post': [ 'perform', // Creates new currency 'prepare', // Prepare creates new currency 'remove/perform', // Removes currency by symbol 's/{symbol}/remove/prepare', // Prepare remove currency by symbol 's/{symbol}/update/perform', // Prepare update currency by symbol 's/{symbol}/update/prepare', // Prepare update currency by symbol ], }, 'markets': { 'get': [ 'i/{id}', // Gets market by id 's/{symbol}', // Gets market by symbol ], 'post': [ 'perform', // Creates new market 'prepare', // Prepare creates new market 'remove/perform', // Removes market by symbol 's/{symbol}/remove/prepare', // Prepare remove market by symbol 's/{symbol}/update/perform', // Prepare update market by symbol 's/{symbol}/update/prepare', // Prepare update market by symbol ], }, 'public': { 'get': [ 'candles', // Gets candles 'currencies', // Gets all the currencies 'markets', // Gets all the markets 'orderbook', // Gets orderbook 'orderbook/raw', // Gets raw orderbook 'orderbook/v2', // Gets orderbook v2 'tickers', // Gets all the tickers 'trades', // Gets trades ], }, 'statistics': { 'get': [ 'address', // calculateAddressStatistics ], }, 'trading': { 'get': [ 'balances', // Get trading balances for all (or selected) currencies 'fees', // Get trading fee rates for all (or selected) markets 'orders', // Gets open orders ], 'post': [ 'orders', // Create new order 'orders/json', // Create orders ], 'put': [ 'orders', // Cancel or update orders 'orders/json', // Update orders ], 'delete': [ 'orders', // Delete orders 'orders/json', // Delete orders ], }, 'tradingview': { 'get': [ 'config', // Gets config 'history', // Gets history 'symbol_info', // Gets symbol info 'time', // Gets time ], }, }, 'exceptions': { 'exact': { '0': ExchangeError, '1': NotSupported, '4000': BadRequest, '4001': BadRequest, '4002': InsufficientFunds, '4003': AuthenticationError, '4004': AuthenticationError, '4005': BadRequest, '4006': BadRequest, '4007': BadRequest, '4300': PermissionDenied, '4100': AuthenticationError, '4400': OrderNotFound, '5001': InvalidOrder, '5002': ExchangeError, '400': BadRequest, '401': AuthenticationError, '403': PermissionDenied, '404': OrderNotFound, '429': RateLimitExceeded, '500': ExchangeError, '503': ExchangeNotAvailable, }, 'broad': { 'Insufficient': InsufficientFunds, }, }, 'options': { 'fetchTickers': { 'period': '1d', }, 'fetchTrades': { 'sort': 'timestamp,asc', }, 'fetchMyTrades': { 'sort': 'timestamp,asc', }, 'fetchOpenOrders': { 'sort': 'createdAt,asc', }, 'fetchClosedOrders': { 'sort': 'createdAt,asc', }, 'defaultSort': 'timestamp,asc', 'defaultSortOrders': 'createdAt,asc', }, }); } async fetchMarkets (params = {}) { const response = await this.publicGetMarkets (params); // // [ // { // "symbol": "ETHBTC", // "name": "ETH/BTC", // "baseCurrency": "ETH", // "baseTokenAddress": "0x45932db54b38af1f5a57136302eeba66a5975c15", // "quoteCurrency": "BTC", // "quoteTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", // "feeCurrency": "BTC", // "feeTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", // "quantityIncrement": "0.0000001", // "takerFee": "0.005", // "makerFee": "0.0025", // "tickSize": "0.00000001", // "baseMinSize": "0.0001", // "quoteMinSize": "0.00001", // "locked": false // } // ] // const result = []; for (let i = 0; i < response.length; i++) { result.push (this.parseMarket (response[i])); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicGetCurrencies (params); // // [ // { // "symbol": "BTC", // "name": "Bitcoin", // "address": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", // "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggb3BhY2l0eT0iMC41IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMwIDUzQzQyLjcwMjUgNTMgNTMgNDIuNzAyNSA1MyAzMEM1MyAxNy4yOTc1IDQyLjcwMjUgNyAzMCA3QzE3LjI5NzUgNyA3IDE3LjI5NzUgNyAzMEM3IDQyLjcwMjUgMTcuMjk3NSA1MyAzMCA1M1pNMzAgNTVDNDMuODA3MSA1NSA1NSA0My44MDcxIDU1IDMwQzU1IDE2LjE5MjkgNDMuODA3MSA1IDMwIDVDMTYuMTkyOSA1IDUgMTYuMTkyOSA1IDMwQzUgNDMuODA3MSAxNi4xOTI5IDU1IDMwIDU1WiIvPgo8cGF0aCBkPSJNNDAuOTQyNSAyNi42NTg1QzQxLjQwMDMgMjMuNjExMyAzOS4wNzA1IDIxLjk3MzIgMzUuODg0OCAyMC44ODA0TDM2LjkxODIgMTYuNzUyNkwzNC4zOTUxIDE2LjEyNjRMMzMuMzg5IDIwLjE0NTVDMzIuNzI1OCAxOS45ODA5IDMyLjA0NDUgMTkuODI1NiAzMS4zNjc1IDE5LjY3MTdMMzIuMzgwOCAxNS42MjYyTDI5Ljg1OTEgMTVMMjguODI1IDE5LjEyNjRDMjguMjc2IDE5LjAwMTkgMjcuNzM3IDE4Ljg3ODggMjcuMjEzOSAxOC43NDkzTDI3LjIxNjggMTguNzM2NEwyMy43MzcyIDE3Ljg3MTJMMjMuMDY2IDIwLjU1NDhDMjMuMDY2IDIwLjU1NDggMjQuOTM4IDIwLjk4MjEgMjQuODk4NSAyMS4wMDg1QzI1LjkyMDQgMjEuMjYyNiAyNi4xMDUgMjEuOTM2IDI2LjA3NDEgMjIuNDY5OUwyNC44OTcgMjcuMTcyNEMyNC45Njc1IDI3LjE5MDMgMjUuMDU4NyAyNy4yMTYgMjUuMTU5MyAyNy4yNTYxQzI1LjA3NTMgMjcuMjM1NCAyNC45ODU0IDI3LjIxMjQgMjQuODkyNyAyNy4xOTAzTDIzLjI0MjggMzMuNzc3OEMyMy4xMTc3IDM0LjA4NjkgMjIuODAwOCAzNC41NTA2IDIyLjA4NjUgMzQuMzc0NkMyMi4xMTE3IDM0LjQxMTEgMjAuMjUyNiAzMy45MTg3IDIwLjI1MjYgMzMuOTE4N0wxOSAzNi43OTQ5TDIyLjI4MzQgMzcuNjFDMjIuODk0MiAzNy43NjI0IDIzLjQ5MjggMzcuOTIyIDI0LjA4MjEgMzguMDcyM0wyMy4wMzggNDIuMjQ3NEwyNS41NTgyIDQyLjg3MzZMMjYuNTkyMyAzOC43NDI5QzI3LjI4MDcgMzguOTI5IDI3Ljk0OSAzOS4xMDA3IDI4LjYwMyAzOS4yNjI0TDI3LjU3MjUgNDMuMzczOEwzMC4wOTU2IDQ0TDMxLjEzOTcgMzkuODMyOEMzNS40NDIyIDQwLjY0MzYgMzguNjc3NCA0MC4zMTY2IDQwLjAzOTIgMzYuNDQxNEM0MS4xMzY1IDMzLjMyMTIgMzkuOTg0NiAzMS41MjEzIDM3LjcyMDkgMzAuMzQ3N0MzOS4zNjk0IDI5Ljk2OTEgNDAuNjExMiAyOC44ODkyIDQwLjk0MjUgMjYuNjU4NVYyNi42NTg1Wk0zNS4xNzc3IDM0LjcwODhDMzQuMzk4IDM3LjgyOSAyOS4xMjI2IDM2LjE0MjIgMjcuNDEyMiAzNS43MTkzTDI4Ljc5NzcgMzAuMTg4MUMzMC41MDgxIDMwLjYxMzIgMzUuOTkyNiAzMS40NTQ4IDM1LjE3NzcgMzQuNzA4OFpNMzUuOTU4MSAyNi42MTM0QzM1LjI0NjcgMjkuNDUxNyAzMC44NTU5IDI4LjAwOTcgMjkuNDMxNiAyNy42NTYxTDMwLjY4NzcgMjIuNjM5NUMzMi4xMTIgMjIuOTkzIDM2LjY5OSAyMy42NTI4IDM1Ljk1ODEgMjYuNjEzNFoiLz4KPC9zdmc+Cg==", // "background": "transparent", // "fiatSymbol": "BTC", // "decimals": 8, // "tradeDecimals": 20, // "displayDecimals": 4, // "crypto": true, // "depositEnabled": true, // "withdrawalEnabled": true, // "transferEnabled": true, // "buyEnabled": false, // "purchaseEnabled": false, // "redeemEnabled": false, // "active": true, // "withdrawalFee": "50000000000000000", // "purchaseCommissions": [] // }, // ] // const result = []; for (let i = 0; i < response.length; i++) { const currency = response[i]; result.push (this.parseCurrency (currency)); } return this.indexBy (result, 'code'); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const period = this.safeString (this.options['fetchTickers'], 'period', '1d'); const request = { 'period': this.timeframes[period], // I1, I5, I15, I30, H1, H2, H4, H6, H12, D1, W1 }; const response = await this.publicGetTickers (this.extend (request, params)); // // [ // { // "ask": 0.017, // "bid": 0.016, // "high": 0.019, // "last": 0.017, // "low": 0.015, // "market": "TIME/ETH", // "open": 0.016, // "period": "H1", // "timestamp": "2018-12-14T20:50:36.134Z", // "volume": 4.57, // "volumeQuote": 0.07312 // } // ] // return this.parseTickers (response, symbols); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const period = this.safeString (this.options['fetchTickers'], 'period', '1d'); const request = { 'market': market['id'], 'period': this.timeframes[period], // I1, I5, I15, I30, H1, H2, H4, H6, H12, D1, W1 }; const response = await this.publicGetTickers (this.extend (request, params)); // // [ // { // "ask": 0.017, // "bid": 0.016, // "high": 0.019, // "last": 0.017, // "low": 0.015, // "market": "TIME/ETH", // "open": 0.016, // "period": "H1", // "timestamp": "2018-12-14T20:50:36.134Z", // "volume": 4.57, // "volumeQuote": 0.07312 // } // ] // const ticker = this.safeValue (response, 0); return this.parseTicker (ticker, market); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetOrderbookV2 (this.extend (request, params)); // // { // "timestamp":"2019-12-05T00:21:09.538", // "bid":[ // { // "index":"2", // "price":"0.02024007", // "baseTokenAmount":"0.0096894", // "baseTokenCumulativeAmount":"0.0096894", // "quoteTokenAmount":"0.000196114134258", // "quoteTokenCumulativeAmount":"0.000196114134258" // }, // "ask":[ // { // "index":"-3", // "price":"0.02024012", // "baseTokenAmount":"0.005", // "baseTokenCumulativeAmount":"0.005", // "quoteTokenAmount":"0.0001012006", // "quoteTokenCumulativeAmount":"0.0001012006" // }, // ] // } // const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); return this.parseOrderBook (response, timestamp, 'bid', 'ask', 'price', 'baseTokenAmount'); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const options = this.safeValue (this.options, 'fetchTrades', {}); const defaultSort = this.safeValue (options, 'sort', 'timestamp,asc'); const sort = this.safeString (params, 'sort', defaultSort); const query = this.omit (params, 'sort'); const request = { // 'address': 'string', // trade’s member account (?) // 'cursor': 1234, // int64 (?) // 'from': this.iso8601 (since), 'market': market['id'], // 'page': 0, // results page you want to retrieve 0 .. N // 'size': limit, // number of records per page, 100 by default 'sort': sort, // array[string], sorting criteria in the format "property,asc" or "property,desc", default is ascending // 'till': this.iso8601 (this.milliseconds ()), }; if (since !== undefined) { request['from'] = this.iso8601 (since); } if (limit !== undefined) { request['size'] = limit; // default is 100 } const response = await this.publicGetTrades (this.extend (request, query)); // // [ // { // "id":1, // "timestamp":"2019-06-25T17:01:50.309", // "direction":"BUY", // "price":"0.027", // "quantity":"0.001" // } // ] // return this.parseTrades (response, market, since, limit); } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'period': this.timeframes[timeframe], }; // if since and limit are not specified const duration = this.parseTimeframe (timeframe); if (since !== undefined) { request['from'] = this.iso8601 (since); if (limit !== undefined) { request['till'] = this.iso8601 (this.sum (since, this.sum (limit, 1) * duration * 1000)); } } else if (limit !== undefined) { const now = this.milliseconds (); request['till'] = this.iso8601 (now); request['from'] = this.iso8601 (now - limit * duration * 1000 - 1); } else { request['till'] = this.iso8601 (this.milliseconds ()); } const response = await this.publicGetCandles (this.extend (request, params)); // // [ // { // "timestamp":"2019-12-04T23:00:00", // "open":"0.02024009", // "high":"0.02024009", // "low":"0.02024009", // "close":"0.02024009", // "volume":"0.00008096036", // "volumeQuote":"0.004", // }, // ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } async fetchBalance (params = {}) { await this.loadMarkets (); const balances = await this.tradingGetBalances (params); // // [ // {"currency":"BTC","totalBalance":"0","lockedBalance":"0"}, // {"currency":"AUDT","totalBalance":"0","lockedBalance":"0"}, // {"currency":"ETH","totalBalance":"0","lockedBalance":"0"}, // {"currency":"TIME","totalBalance":"0","lockedBalance":"0"}, // {"currency":"USDT","totalBalance":"0","lockedBalance":"0"} // ] // const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['total'] = this.safeFloat (balance, 'totalBalance'); account['used'] = this.safeFloat (balance, 'lockedBalance'); result[code] = account; } return this.parseBalance (result); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'symbol': market['id'], 'quantity': this.amountToPrecision (symbol, amount), 'side': side.toUpperCase (), // 'clientOrderId': '123', // 'expireIn': 1575523308, // in seconds // 'expireTime': 1575523308, // unix timestamp }; let query = params; if (type === 'limit') { request['price'] = this.priceToPrecision (symbol, price); const defaultExpireIn = this.safeInteger (this.options, 'expireIn'); const expireTime = this.safeValue (params, 'expireTime'); const expireIn = this.safeValue (params, 'expireIn', defaultExpireIn); if (expireTime !== undefined) { request['expireTime'] = expireTime; } else if (expireIn !== undefined) { request['expireIn'] = expireIn; } else { throw new InvalidOrder (this.id + ' createOrder method requires a expireTime or expireIn param for a ' + type + ' order, you can also set the expireIn exchange-wide option'); } query = this.omit (params, [ 'expireTime', 'expireIn' ]); } else { request['price'] = 0; } const response = await this.tradingPostOrders (this.extend (request, query)); // // { // "orders": [ // { // "cancelledQuantity": "0.3", // "clientOrderId": "my-order-1", // "createdAt": "1970-01-01T00:00:00", // "cursorId": 50, // "expireTime": "1970-01-01T00:00:00", // "filledQuantity": "0.3", // "id": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "type": "LIMIT", // "updatedAt": "1970-01-01T00:00:00" // } // ] // } // const orders = this.safeValue (response, 'orders', []); const order = this.safeValue (orders, 0, {}); return this.parseOrder (order, market); } async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'id': id, }; if (amount !== undefined) { request['quantity'] = this.amountToPrecision (symbol, amount); } if (price !== undefined) { request['price'] = this.priceToPrecision (symbol, price); } const response = await this.tradingPutOrders (this.extend (request, params)); // // { // "changedOrders": [ // { // "newOrder": { // "cancelledQuantity": "0.3", // "clientOrderId": "my-order-1", // "createdAt": "1970-01-01T00:00:00", // "cursorId": 50, // "expireTime": "1970-01-01T00:00:00", // "filledQuantity": "0.3", // "id": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "type": "LIMIT", // "updatedAt": "1970-01-01T00:00:00" // }, // "oldId": "string", // }, // ], // "unchangedOrders": [ "string" ], // } // if ('unchangedOrders' in response) { const orderIds = this.safeValue (response, 'unchangedOrders', []); const orderId = this.safeString (orderIds, 0); return { 'id': orderId, 'info': response, }; } const orders = this.safeValue (response, 'changedOrders', []); const firstOrder = this.safeValue (orders, 0, {}); const order = this.safeValue (firstOrder, 'newOrder', {}); return this.parseOrder (order, market); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); return await this.cancelOrders ([ id ], symbol, params); } async cancelOrders (ids, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': ids, }; const response = await this.tradingDeleteOrders (this.extend (request, params)); // // { // "changedOrders": [ // { // "newOrder": { // "cancelledQuantity": "0.3", // "clientOrderId": "my-order-1", // "createdAt": "1970-01-01T00:00:00", // "cursorId": 50, // "expireTime": "1970-01-01T00:00:00", // "filledQuantity": "0.3", // "id": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "type": "LIMIT", // "updatedAt": "1970-01-01T00:00:00" // }, // "oldId": "string", // }, // ], // "unchangedOrders": [ "string" ], // } return response; } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'orderHash': id, }; const response = await this.historyGetOrdersDetails (request); // // { // "order": { // "cancelledQuantity": "0.3", // "clientOrderId": "my-order-1", // "createdAt": "1970-01-01T00:00:00", // "cursorId": 50, // "expireTime": "1970-01-01T00:00:00", // "filledQuantity": "0.3", // "id": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "type": "LIMIT", // "updatedAt": "1970-01-01T00:00:00" // }, // "trades": [ // { // "fee": "0.3", // "id": 100, // "makerOrTaker": "MAKER", // "makerOrderId": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "takerOrderId": "string", // "timestamp": "2019-12-05T07:48:26.310Z" // } // ] // } // const order = this.safeValue (response, 'order', {}); const trades = this.safeValue (response, 'trades', []); return this.parseOrder (this.extend (order, { 'trades': trades })); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const options = this.safeValue (this.options, 'fetchOpenOrders', {}); const defaultSort = this.safeValue (options, 'sort', 'createdAt,asc'); const sort = this.safeString (params, 'sort', defaultSort); const query = this.omit (params, 'sort'); const request = { // 'clientOrderId': '123', // order’s client id list for filter // page: 0, // results page you want to retrieve (0 .. N) 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['size'] = limit; } const response = await this.tradingGetOrders (this.extend (request, query)); // // { // "orders": [ // { // "cancelledQuantity": "0.3", // "clientOrderId": "my-order-1", // "createdAt": "1970-01-01T00:00:00", // "cursorId": 50, // "expireTime": "1970-01-01T00:00:00", // "filledQuantity": "0.3", // "id": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "type": "LIMIT", // "updatedAt": "1970-01-01T00:00:00" // } // ] // } // const orders = this.safeValue (response, 'orders', []); return this.parseOrders (orders, market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const options = this.safeValue (this.options, 'fetchClosedOrders', {}); const defaultSort = this.safeValue (options, 'sort', 'createdAt,asc'); const sort = this.safeString (params, 'sort', defaultSort); const query = this.omit (params, 'sort'); const request = { // 'clientOrderId': '123', // order’s client id list for filter // page: 0, // results page you want to retrieve (0 .. N) 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported 'side': 'BUY', // or 'SELL' // 'till': this.iso8601 (this.milliseconds ()), }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['from'] = this.iso8601 (since); } if (limit !== undefined) { request['size'] = limit; } const response = await this.historyGetOrders (this.extend (request, query)); // // { // "orders": [ // { // "cancelledQuantity": "0.3", // "clientOrderId": "my-order-1", // "createdAt": "1970-01-01T00:00:00", // "cursorId": 50, // "expireTime": "1970-01-01T00:00:00", // "filledQuantity": "0.3", // "id": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "type": "LIMIT", // "updatedAt": "1970-01-01T00:00:00" // } // ] // } // const orders = this.safeValue (response, 'orders', []); return this.parseOrders (orders, market, since, limit); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const options = this.safeValue (this.options, 'fetchMyTrades', {}); const defaultSort = this.safeValue (options, 'sort', 'timestamp,asc'); const sort = this.safeString (params, 'sort', defaultSort); const query = this.omit (params, 'sort'); const request = { // 'cursorId': 123, // int64 (?) // 'from': this.iso8601 (since), // 'makerOrderId': '1234', // maker order hash // 'owner': '...', // owner address (?) // 'page': 0, // results page you want to retrieve (0 .. N) // 'side': 'BUY', // or 'SELL' // 'size': limit, 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported // 'symbol': market['id'], // 'takerOrderId': '1234', // 'till': this.iso8601 (this.milliseconds ()), }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['from'] = this.iso8601 (since); } if (limit !== undefined) { request['size'] = limit; } const response = await this.historyGetTrades (this.extend (request, query)); // // { // "trades": [ // { // "fee": "0.3", // "id": 100, // "makerOrTaker": "MAKER", // "makerOrderId": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "takerOrderId": "string", // "timestamp": "2019-12-08T04:54:11.171Z" // } // ] // } // const trades = this.safeValue (response, 'trades', []); return this.parseTrades (trades, market, since, limit); } async fetchTradingFee (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'markets': market['id'], }; const response = await this.tradingGetFees (this.extend (request, params)); // // [ // { // "fee": 0.0075, // "market": "ETHBTC" // } // ] // const result = this.safeValue (response, 0, {}); return { 'info': response, 'maker': this.safeFloat (result, 'fee'), 'taker': undefined, }; } parseMarket (market) { // // { // "symbol": "ETHBTC", // "name": "ETH/BTC", // "baseCurrency": "ETH", // "baseTokenAddress": "0x45932db54b38af1f5a57136302eeba66a5975c15", // "quoteCurrency": "BTC", // "quoteTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", // "feeCurrency": "BTC", // "feeTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", // "quantityIncrement": "0.0000001", // "takerFee": "0.005", // "makerFee": "0.0025", // "tickSize": "0.00000001", // "baseMinSize": "0.0001", // "quoteMinSize": "0.00001", // "locked": false // } // const locked = this.safeValue (market, 'locked'); const active = !locked; const id = this.safeString (market, 'symbol'); const baseId = this.safeString (market, 'baseCurrency'); const quoteId = this.safeString (market, 'quoteCurrency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.precisionFromString (this.safeString (market, 'quantityIncrement')), 'price': this.precisionFromString (this.safeString (market, 'tickSize')), }; const amountIncrement = this.safeFloat (market, 'quantityIncrement'); const minBase = this.safeFloat (market, 'baseMinSize'); const minAmount = Math.max (amountIncrement, minBase); const priceIncrement = this.safeFloat (market, 'tickSize'); const minCost = this.safeFloat (market, 'quoteMinSize'); const limits = { 'amount': { 'min': minAmount, 'max': undefined }, 'price': { 'min': priceIncrement, 'max': undefined }, 'cost': { 'min': Math.max (minCost, minAmount * priceIncrement), 'max': undefined }, }; const taker = this.safeFloat (market, 'takerFee'); const maker = this.safeFloat (market, 'makerFee'); return { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'type': 'spot', 'active': active, 'precision': precision, 'limits': limits, 'taker': taker, 'maker': maker, 'info': market, }; } parseCurrency (currency) { // // { // "symbol": "BTC", // "name": "Bitcoin", // "address": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", // "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR...mc+Cg==", // "background": "transparent", // "fiatSymbol": "BTC", // "decimals": 8, // "tradeDecimals": 20, // "displayDecimals": 4, // "crypto": true, // "depositEnabled": true, // "withdrawalEnabled": true, // "transferEnabled": true, // "buyEnabled": false, // "purchaseEnabled": false, // "redeemEnabled": false, // "active": true, // "withdrawalFee": "50000000000000000", // "purchaseCommissions": [] // } // // https://github.com/ccxt/ccxt/issues/6878 // // { // "symbol":"XRP", // "name":"Ripple", // "address":"0x0dc8882914f3ddeebf4cec6dc20edb99df3def6c", // "decimals":6, // "tradeDecimals":16, // "depositEnabled":true, // "withdrawalEnabled":true, // "transferEnabled":true, // "active":true // } // const id = this.safeString (currency, 'symbol'); const code = this.safeCurrencyCode (id); const name = this.safeString (currency, 'name'); const precision = this.safeInteger (currency, 'decimals'); const active = this.safeValue (currency, 'active'); // const fee = this.safeFloat (currency, 'withdrawalFee'); const feeString = this.safeString (currency, 'withdrawalFee'); const tradeDecimals = this.safeInteger (currency, 'tradeDecimals'); let fee = undefined; if ((feeString !== undefined) && (tradeDecimals !== undefined)) { const feeStringLen = feeString.length; const dotIndex = feeStringLen - tradeDecimals; if (dotIndex > 0) { const whole = feeString.slice (0, dotIndex); const fraction = feeString.slice (-dotIndex); fee = parseFloat (whole + '.' + fraction); } else { let fraction = '.'; for (let i = 0; i < -dotIndex; i++) { fraction += '0'; } fee = parseFloat (fraction + feeString); } } return { 'id': code, 'code': code, 'info': currency, 'type': undefined, 'name': name, 'active': active, 'fee': fee, 'precision': precision, 'limits': { 'withdraw': { 'min': fee, 'max': undefined }, 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, }, }; } parseTickers (rawTickers, symbols = undefined) { const tickers = []; for (let i = 0; i < rawTickers.length; i++) { tickers.push (this.parseTicker (rawTickers[i])); } return this.filterByArray (tickers, 'symbol', symbols); } parseTicker (ticker, market = undefined) { // // { // "ask": 0.017, // "bid": 0.016, // "high": 0.019, // "last": 0.017, // "low": 0.015, // "market": "TIME/ETH", // "open": 0.016, // "period": "H1", // "timestamp": "2018-12-14T20:50:36.134Z", // "volume": 4.57, // "volumeQuote": 0.07312 // } // let symbol = undefined; const marketId = this.safeString (ticker, 'market'); if (marketId !== undefined) { if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); const last = this.safeFloat (ticker, 'last'); const open = this.safeFloat (ticker, 'open'); let change = undefined; let average = undefined; if (last !== undefined && open !== undefined) { change = last - open; average = this.sum (last, open) / 2; } let percentage = undefined; if (change !== undefined && open) { percentage = (change / open) * 100; } return { 'symbol': symbol, 'info': ticker, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': this.safeFloat (ticker, 'volumeQuote'), }; } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // { // "id":1, // "timestamp":"2019-06-25T17:01:50.309", // "direction":"BUY", // "price":"0.027", // "quantity":"0.001" // } // // fetchMyTrades, fetchOrder (private) // // { // "fee": "0.3", // "id": 100, // "makerOrTaker": "MAKER", // "makerOrderId": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "takerOrderId": "string", // "timestamp": "2019-12-08T04:54:11.171Z" // } // let symbol = undefined; const marketId = this.safeString (trade, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'quantity'); const id = this.safeString (trade, 'id'); const side = this.safeStringLower2 (trade, 'direction', 'side'); const takerOrMaker = this.safeStringLower (trade, 'makerOrTaker'); let orderId = undefined; if (takerOrMaker !== undefined) { orderId = this.safeString (trade, takerOrMaker + 'OrderId'); } let fee = undefined; const feeCost = this.safeFloat (trade, 'fee'); if (feeCost !== undefined) { const feeCurrency = (market === undefined) ? undefined : market['quote']; fee = { 'cost': feeCost, 'currency': feeCurrency, }; } let cost = undefined; if ((price !== undefined) && (amount !== undefined)) { cost = this.costToPrecision (symbol, amount * price); } return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': orderId, 'type': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'takerOrMaker': takerOrMaker, 'fee': fee, }; } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { // // { // "timestamp":"2019-12-04T23:00:00", // "open":"0.02024009", // "high":"0.02024009", // "low":"0.02024009", // "close":"0.02024009", // "volume":"0.00008096036", // "volumeQuote":"0.004", // } // return [ this.parse8601 (this.safeString (ohlcv, 'timestamp')), this.safeFloat (ohlcv, 'open'), this.safeFloat (ohlcv, 'high'), this.safeFloat (ohlcv, 'low'), this.safeFloat (ohlcv, 'close'), this.safeFloat (ohlcv, 'volume'), ]; } parseOrder (order, market = undefined) { // // fetchOrder, createOrder, cancelOrder, cancelOrders, fetchOpenOrders, fetchClosedOrders // // { // "cancelledQuantity": "0.3", // "clientOrderId": "my-order-1", // "createdAt": "1970-01-01T00:00:00", // "cursorId": 50, // "expireTime": "1970-01-01T00:00:00", // "filledQuantity": "0.3", // "id": "string", // "price": "0.017", // "quantity": "0.3", // "side": "BUY", // "symbol": "TIMEETH", // "type": "LIMIT", // "updatedAt": "1970-01-01T00:00:00" // "trades": [], // injected from the outside // } // const id = this.safeString (order, 'id'); const type = this.safeStringLower (order, 'type'); const side = this.safeStringLower (order, 'side'); let symbol = undefined; const marketId = this.safeString (order, 'symbol'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } if ((symbol === undefined) && (market !== undefined)) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString (order, 'createdAt')); const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'quantity'); const filled = this.safeFloat (order, 'filledQuantity'); const canceledQuantity = this.safeFloat (order, 'cancelledQuantity'); let remaining = undefined; let status = undefined; if ((amount !== undefined) && (filled !== undefined)) { remaining = Math.max (amount - filled, 0.0); if (filled >= amount) { status = 'closed'; } else if ((canceledQuantity !== undefined) && (canceledQuantity > 0)) { status = 'canceled'; } else { status = 'open'; } } const cost = parseFloat (this.costToPrecision (symbol, price * filled)); const fee = undefined; let lastTradeTimestamp = undefined; let trades = undefined; const rawTrades = this.safeValue (order, 'trades'); if (rawTrades !== undefined) { trades = this.parseTrades (rawTrades, market, undefined, undefined, { 'order': id, }); } if (trades !== undefined) { const numTrades = trades.length; if (numTrades > 0) { lastTradeTimestamp = trades[numTrades - 1]['timestamp']; } } const clientOrderId = this.safeString (order, 'clientOrderId'); return { 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'average': undefined, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': trades, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/' + api + '/' + path; if (Object.keys (params).length) { url += '?' + this.urlencodeWithArrayRepeat (params); } if (api !== 'public') { this.checkRequiredCredentials (); const auth = this.stringToBase64 (this.encode (this.apiKey + ':' + this.secret)); const secret = 'Basic ' + this.decode (auth); headers = { 'authorization': secret }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { if (response === undefined) { return; } if (statusCode >= 400) { // // {"error":{"timestamp":"05.12.2019T05:25:43.584+0000","status":"BAD_REQUEST","message":"Insufficient ETH balance. Required: 1, actual: 0.","code":4001}} // {"error":{"timestamp":"05.12.2019T04:03:25.419+0000","status":"FORBIDDEN","message":"Access denied","code":4300}} // const feedback = this.id + ' ' + responseBody; let error = this.safeValue (response, 'error'); if (error === undefined) { error = response; } const code = this.safeString2 (error, 'code', 'status'); const message = this.safeString2 (error, 'message', 'debugMessage'); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9}],164:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const bw = require ('./bw.js'); // --------------------------------------------------------------------------- module.exports = class topq extends bw { describe () { return this.deepExtend (super.describe (), { 'id': 'topq', 'countries': [ 'SG' ], 'name': 'TOP.Q', 'hostname': 'topliq.com', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/74596147-50247000-505c-11ea-9224-4fd347cfbb49.jpg', 'api': 'https://www.{hostname}', 'www': 'https://www.topliq.com', 'doc': 'https://github.com/topq-exchange/api_docs_en/wiki/REST_api_reference', 'fees': 'https://www.topliq.com/feesRate', }, }); } }; },{"./bw.js":55}],165:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, BadRequest, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, PermissionDenied, AddressPending } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class upbit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'upbit', 'name': 'Upbit', 'countries': [ 'KR' ], 'version': 'v1', 'rateLimit': 1000, 'certified': true, 'pro': true, // new metainfo interface 'has': { 'CORS': true, 'createDepositAddress': true, 'createMarketOrder': true, 'fetchDepositAddress': true, 'fetchClosedOrders': true, 'fetchMyTrades': false, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrderBooks': true, 'fetchOpenOrders': true, 'fetchOrders': false, 'fetchTickers': true, 'withdraw': true, 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTransactions': false, }, 'timeframes': { '1m': 'minutes', '3m': 'minutes', '5m': 'minutes', '15m': 'minutes', '30m': 'minutes', '1h': 'minutes', '4h': 'minutes', '1d': 'days', '1w': 'weeks', '1M': 'months', }, 'hostname': 'api.upbit.com', 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg', 'api': { 'public': 'https://{hostname}', 'private': 'https://{hostname}', }, 'www': 'https://upbit.com', 'doc': 'https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C', 'fees': 'https://upbit.com/service_center/guide', }, 'api': { 'public': { 'get': [ 'market/all', 'candles/{timeframe}', 'candles/{timeframe}/{unit}', 'candles/minutes/{unit}', 'candles/minutes/1', 'candles/minutes/3', 'candles/minutes/5', 'candles/minutes/15', 'candles/minutes/30', 'candles/minutes/60', 'candles/minutes/240', 'candles/days', 'candles/weeks', 'candles/months', 'trades/ticks', 'ticker', 'orderbook', ], }, 'private': { 'get': [ 'accounts', 'orders/chance', 'order', 'orders', 'withdraws', 'withdraw', 'withdraws/chance', 'deposits', 'deposit', 'deposits/coin_addresses', 'deposits/coin_address', ], 'post': [ 'orders', 'withdraws/coin', 'withdraws/krw', 'deposits/generate_coin_address', ], 'delete': [ 'order', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': 0.0025, 'taker': 0.0025, }, 'funding': { 'tierBased': false, 'percentage': false, 'withdraw': {}, 'deposit': {}, }, }, 'exceptions': { 'exact': { 'This key has expired.': AuthenticationError, 'Missing request parameter error. Check the required parameters!': BadRequest, 'side is missing, side does not have a valid value': InvalidOrder, }, 'broad': { 'thirdparty_agreement_required': PermissionDenied, 'out_of_scope': PermissionDenied, 'order_not_found': OrderNotFound, 'insufficient_funds': InsufficientFunds, 'invalid_access_key': AuthenticationError, 'jwt_verification': AuthenticationError, 'create_ask_error': ExchangeError, 'create_bid_error': ExchangeError, 'volume_too_large': InvalidOrder, 'invalid_funds': InvalidOrder, }, }, 'options': { 'createMarketBuyOrderRequiresPrice': true, 'fetchTickersMaxLength': 4096, // 2048, 'fetchOrderBooksMaxLength': 4096, // 2048, 'symbolSeparator': '-', 'tradingFeesByQuoteCurrency': { 'KRW': 0.0005, }, }, }); } async fetchCurrency (code, params = {}) { // this method is for retrieving funding fees and limits per currency // it requires private access and API keys properly set up await this.loadMarkets (); const currency = this.currency (code); return await this.fetchCurrencyById (currency['id'], params); } async fetchCurrencyById (id, params = {}) { // this method is for retrieving funding fees and limits per currency // it requires private access and API keys properly set up const request = { 'currency': id, }; const response = await this.privateGetWithdrawsChance (this.extend (request, params)); // // { // "member_level": { // "security_level": 3, // "fee_level": 0, // "email_verified": true, // "identity_auth_verified": true, // "bank_account_verified": true, // "kakao_pay_auth_verified": false, // "locked": false, // "wallet_locked": false // }, // "currency": { // "code": "BTC", // "withdraw_fee": "0.0005", // "is_coin": true, // "wallet_state": "working", // "wallet_support": [ "deposit", "withdraw" ] // }, // "account": { // "currency": "BTC", // "balance": "10.0", // "locked": "0.0", // "avg_krw_buy_price": "8042000", // "modified": false // }, // "withdraw_limit": { // "currency": "BTC", // "minimum": null, // "onetime": null, // "daily": "10.0", // "remaining_daily": "10.0", // "remaining_daily_krw": "0.0", // "fixed": null, // "can_withdraw": true // } // } // const memberInfo = this.safeValue (response, 'member_level', {}); const currencyInfo = this.safeValue (response, 'currency', {}); const withdrawLimits = this.safeValue (response, 'withdraw_limit', {}); const canWithdraw = this.safeValue (withdrawLimits, 'can_withdraw'); const walletState = this.safeString (currencyInfo, 'wallet_state'); const walletLocked = this.safeValue (memberInfo, 'wallet_locked'); const locked = this.safeValue (memberInfo, 'locked'); let active = true; if ((canWithdraw !== undefined) && canWithdraw) { active = false; } else if (walletState !== 'working') { active = false; } else if ((walletLocked !== undefined) && walletLocked) { active = false; } else if ((locked !== undefined) && locked) { active = false; } const maxOnetimeWithdrawal = this.safeFloat (withdrawLimits, 'onetime'); const maxDailyWithdrawal = this.safeFloat (withdrawLimits, 'daily', maxOnetimeWithdrawal); const remainingDailyWithdrawal = this.safeFloat (withdrawLimits, 'remaining_daily', maxDailyWithdrawal); let maxWithdrawLimit = undefined; if (remainingDailyWithdrawal > 0) { maxWithdrawLimit = remainingDailyWithdrawal; } else { maxWithdrawLimit = maxDailyWithdrawal; } const precision = undefined; const currencyId = this.safeString (currencyInfo, 'code'); const code = this.safeCurrencyCode (currencyId); return { 'info': response, 'id': currencyId, 'code': code, 'name': code, 'active': active, 'fee': this.safeFloat (currencyInfo, 'withdraw_fee'), 'precision': precision, 'limits': { 'withdraw': { 'min': this.safeFloat (withdrawLimits, 'minimum'), 'max': maxWithdrawLimit, }, }, }; } async fetchMarket (symbol, params = {}) { // this method is for retrieving trading fees and limits per market // it requires private access and API keys properly set up await this.loadMarkets (); const market = this.market (symbol); return await this.fetchMarketById (market['id'], params); } async fetchMarketById (id, params = {}) { // this method is for retrieving trading fees and limits per market // it requires private access and API keys properly set up const request = { 'market': id, }; const response = await this.privateGetOrdersChance (this.extend (request, params)); // // { bid_fee: "0.0005", // ask_fee: "0.0005", // market: { id: "KRW-BTC", // name: "BTC/KRW", // order_types: ["limit"], // order_sides: ["ask", "bid"], // bid: { currency: "KRW", // price_unit: null, // min_total: 1000 }, // ask: { currency: "BTC", // price_unit: null, // min_total: 1000 }, // max_total: "1000000000.0", // state: "active" }, // bid_account: { currency: "KRW", // balance: "0.0", // locked: "0.0", // avg_krw_buy_price: "0", // modified: false }, // ask_account: { currency: "BTC", // balance: "0.00780836", // locked: "0.0", // avg_krw_buy_price: "6465564.67", // modified: false } } // const marketInfo = this.safeValue (response, 'market'); const bid = this.safeValue (marketInfo, 'bid'); const ask = this.safeValue (marketInfo, 'ask'); const marketId = this.safeString (marketInfo, 'id'); const baseId = this.safeString (ask, 'currency'); const quoteId = this.safeString (bid, 'currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': 8, 'price': 8, }; const state = this.safeString (marketInfo, 'state'); const active = (state === 'active'); const bidFee = this.safeFloat (response, 'bid_fee'); const askFee = this.safeFloat (response, 'ask_fee'); const fee = Math.max (bidFee, askFee); return { 'info': response, 'id': marketId, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'precision': precision, 'maker': fee, 'taker': fee, 'limits': { 'amount': { 'min': this.safeFloat (ask, 'min_total'), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': this.safeFloat (bid, 'min_total'), 'max': this.safeFloat (marketInfo, 'max_total'), }, }, }; } async fetchMarkets (params = {}) { const response = await this.publicGetMarketAll (params); // // [ { market: "KRW-BTC", // korean_name: "비트코인", // english_name: "Bitcoin" }, // { market: "KRW-DASH", // korean_name: "대시", // english_name: "Dash" }, // { market: "KRW-ETH", // korean_name: "이더리움", // english_name: "Ethereum" }, // { market: "BTC-ETH", // korean_name: "이더리움", // english_name: "Ethereum" }, // ..., // { market: "BTC-BSV", // korean_name: "비트코인에스브이", // english_name: "Bitcoin SV" } ] // const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'market'); const [ quoteId, baseId ] = id.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': 8, 'price': 8, }; const active = true; const makerFee = this.safeFloat (this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['maker']); const takerFee = this.safeFloat (this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['taker']); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'info': market, 'precision': precision, 'maker': makerFee, 'taker': takerFee, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetAccounts (params); // // [ { currency: "BTC", // balance: "0.005", // locked: "0.0", // avg_krw_buy_price: "7446000", // modified: false }, // { currency: "ETH", // balance: "0.1", // locked: "0.0", // avg_krw_buy_price: "250000", // modified: false } ] // const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'balance'); account['used'] = this.safeFloat (balance, 'locked'); result[code] = account; } return this.parseBalance (result); } getSymbolFromMarketId (marketId, market = undefined) { if (marketId === undefined) { return undefined; } market = this.safeValue (this.markets_by_id, marketId, market); if (market !== undefined) { return market['symbol']; } const [ baseId, quoteId ] = marketId.split (this.options['symbolSeparator']); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); return base + '/' + quote; } async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let ids = undefined; if (symbols === undefined) { ids = this.ids.join (','); // max URL length is 2083 symbols, including http schema, hostname, tld, etc... if (ids.length > this.options['fetchOrderBooksMaxLength']) { const numIds = this.ids.length; throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols (' + ids.length.toString () + ' characters) exceeding max URL length (' + this.options['fetchOrderBooksMaxLength'].toString () + ' characters), you are required to specify a list of symbols in the first argument to fetchOrderBooks'); } } else { ids = this.marketIds (symbols); ids = ids.join (','); } const request = { 'markets': ids, }; const response = await this.publicGetOrderbook (this.extend (request, params)); // // [ { market: "BTC-ETH", // timestamp: 1542899030043, // total_ask_size: 109.57065201, // total_bid_size: 125.74430631, // orderbook_units: [ { ask_price: 0.02926679, // bid_price: 0.02919904, // ask_size: 4.20293961, // bid_size: 11.65043576 }, // ..., // { ask_price: 0.02938209, // bid_price: 0.0291231, // ask_size: 0.05135782, // bid_size: 13.5595 } ] }, // { market: "KRW-BTC", // timestamp: 1542899034662, // total_ask_size: 12.89790974, // total_bid_size: 4.88395783, // orderbook_units: [ { ask_price: 5164000, // bid_price: 5162000, // ask_size: 2.57606495, // bid_size: 0.214 }, // ..., // { ask_price: 5176000, // bid_price: 5152000, // ask_size: 2.752, // bid_size: 0.4650305 } ] } ] // const result = {}; for (let i = 0; i < response.length; i++) { const orderbook = response[i]; const symbol = this.getSymbolFromMarketId (this.safeString (orderbook, 'market')); const timestamp = this.safeInteger (orderbook, 'timestamp'); result[symbol] = { 'bids': this.sortBy (this.parseBidsAsks (orderbook['orderbook_units'], 'bid_price', 'bid_size'), 0, true), 'asks': this.sortBy (this.parseBidsAsks (orderbook['orderbook_units'], 'ask_price', 'ask_size'), 0), 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'nonce': undefined, }; } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { const orderbooks = await this.fetchOrderBooks ([ symbol ], limit, params); return this.safeValue (orderbooks, symbol); } parseTicker (ticker, market = undefined) { // // { market: "BTC-ETH", // trade_date: "20181122", // trade_time: "104543", // trade_date_kst: "20181122", // trade_time_kst: "194543", // trade_timestamp: 1542883543097, // opening_price: 0.02976455, // high_price: 0.02992577, // low_price: 0.02934283, // trade_price: 0.02947773, // prev_closing_price: 0.02966, // change: "FALL", // change_price: 0.00018227, // change_rate: 0.0061453136, // signed_change_price: -0.00018227, // signed_change_rate: -0.0061453136, // trade_volume: 1.00000005, // acc_trade_price: 100.95825586, // acc_trade_price_24h: 289.58650166, // acc_trade_volume: 3409.85311036, // acc_trade_volume_24h: 9754.40510513, // highest_52_week_price: 0.12345678, // highest_52_week_date: "2018-02-01", // lowest_52_week_price: 0.023936, // lowest_52_week_date: "2017-12-08", // timestamp: 1542883543813 } // const timestamp = this.safeInteger (ticker, 'trade_timestamp'); const symbol = this.getSymbolFromMarketId (this.safeString2 (ticker, 'market', 'code'), market); const previous = this.safeFloat (ticker, 'prev_closing_price'); const last = this.safeFloat (ticker, 'trade_price'); const change = this.safeFloat (ticker, 'signed_change_price'); const percentage = this.safeFloat (ticker, 'signed_change_rate'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high_price'), 'low': this.safeFloat (ticker, 'low_price'), 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'opening_price'), 'close': last, 'last': last, 'previousClose': previous, 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'acc_trade_volume_24h'), 'quoteVolume': this.safeFloat (ticker, 'acc_trade_price_24h'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); let ids = undefined; if (symbols === undefined) { ids = this.ids.join (','); // max URL length is 2083 symbols, including http schema, hostname, tld, etc... if (ids.length > this.options['fetchTickersMaxLength']) { const numIds = this.ids.length; throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchTickers'); } } else { ids = this.marketIds (symbols); ids = ids.join (','); } const request = { 'markets': ids, }; const response = await this.publicGetTicker (this.extend (request, params)); // // [ { market: "BTC-ETH", // trade_date: "20181122", // trade_time: "104543", // trade_date_kst: "20181122", // trade_time_kst: "194543", // trade_timestamp: 1542883543097, // opening_price: 0.02976455, // high_price: 0.02992577, // low_price: 0.02934283, // trade_price: 0.02947773, // prev_closing_price: 0.02966, // change: "FALL", // change_price: 0.00018227, // change_rate: 0.0061453136, // signed_change_price: -0.00018227, // signed_change_rate: -0.0061453136, // trade_volume: 1.00000005, // acc_trade_price: 100.95825586, // acc_trade_price_24h: 289.58650166, // acc_trade_volume: 3409.85311036, // acc_trade_volume_24h: 9754.40510513, // highest_52_week_price: 0.12345678, // highest_52_week_date: "2018-02-01", // lowest_52_week_price: 0.023936, // lowest_52_week_date: "2017-12-08", // timestamp: 1542883543813 } ] // const result = {}; for (let t = 0; t < response.length; t++) { const ticker = this.parseTicker (response[t]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchTicker (symbol, params = {}) { const tickers = await this.fetchTickers ([ symbol ], params); return this.safeValue (tickers, symbol); } parseTrade (trade, market = undefined) { // // fetchTrades // // { market: "BTC-ETH", // trade_date_utc: "2018-11-22", // trade_time_utc: "13:55:24", // timestamp: 1542894924397, // trade_price: 0.02914289, // trade_volume: 0.20074397, // prev_closing_price: 0.02966, // change_price: -0.00051711, // ask_bid: "ASK", // sequential_id: 15428949259430000 } // // fetchOrder trades // // { // "market": "KRW-BTC", // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", // "price": "101000.0", // "volume": "0.77368323", // "funds": "78142.00623", // "ask_fee": "117.213009345", // "bid_fee": "117.213009345", // "created_at": "2018-04-05T14:09:15+09:00", // "side": "bid", // } // const id = this.safeString2 (trade, 'sequential_id', 'uuid'); const orderId = undefined; let timestamp = this.safeInteger (trade, 'timestamp'); if (timestamp === undefined) { timestamp = this.parse8601 (this.safeString (trade, 'created_at')); } let side = undefined; const askOrBid = this.safeStringLower2 (trade, 'ask_bid', 'side'); if (askOrBid === 'ask') { side = 'sell'; } else if (askOrBid === 'bid') { side = 'buy'; } let cost = this.safeFloat (trade, 'funds'); const price = this.safeFloat2 (trade, 'trade_price', 'price'); const amount = this.safeFloat2 (trade, 'trade_volume', 'volume'); if (cost === undefined) { if (amount !== undefined) { if (price !== undefined) { cost = price * amount; } } } const marketId = this.safeString2 (trade, 'market', 'code'); market = this.safeValue (this.markets_by_id, marketId, market); let fee = undefined; let feeCurrency = undefined; let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; feeCurrency = market['quote']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; feeCurrency = quote; } const feeCost = this.safeString (trade, askOrBid + '_fee'); if (feeCost !== undefined) { fee = { 'currency': feeCurrency, 'cost': feeCost, }; } return { 'id': id, 'info': trade, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': 'limit', 'side': side, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 200; } const request = { 'market': market['id'], 'count': limit, }; const response = await this.publicGetTradesTicks (this.extend (request, params)); // // [ { market: "BTC-ETH", // trade_date_utc: "2018-11-22", // trade_time_utc: "13:55:24", // timestamp: 1542894924397, // trade_price: 0.02914289, // trade_volume: 0.20074397, // prev_closing_price: 0.02966, // change_price: -0.00051711, // ask_bid: "ASK", // sequential_id: 15428949259430000 }, // { market: "BTC-ETH", // trade_date_utc: "2018-11-22", // trade_time_utc: "13:03:10", // timestamp: 1542891790123, // trade_price: 0.02917, // trade_volume: 7.392, // prev_closing_price: 0.02966, // change_price: -0.00049, // ask_bid: "ASK", // sequential_id: 15428917910540000 } ] // return this.parseTrades (response, market, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { // // { market: "BTC-ETH", // candle_date_time_utc: "2018-11-22T13:47:00", // candle_date_time_kst: "2018-11-22T22:47:00", // opening_price: 0.02915963, // high_price: 0.02915963, // low_price: 0.02915448, // trade_price: 0.02915448, // timestamp: 1542894473674, // candle_acc_trade_price: 0.0981629437535248, // candle_acc_trade_volume: 3.36693173, // unit: 1 }, // return [ this.parse8601 (this.safeString (ohlcv, 'candle_date_time_utc')), this.safeFloat (ohlcv, 'opening_price'), this.safeFloat (ohlcv, 'high_price'), this.safeFloat (ohlcv, 'low_price'), this.safeFloat (ohlcv, 'trade_price'), this.safeFloat (ohlcv, 'candle_acc_trade_volume'), // base volume ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const timeframePeriod = this.parseTimeframe (timeframe); const timeframeValue = this.timeframes[timeframe]; if (limit === undefined) { limit = 200; } const request = { 'market': market['id'], 'timeframe': timeframeValue, 'count': limit, }; let method = 'publicGetCandlesTimeframe'; if (timeframeValue === 'minutes') { const numMinutes = Math.round (timeframePeriod / 60); request['unit'] = numMinutes; method += 'Unit'; } if (since !== undefined) { // convert `since` to `to` value request['to'] = this.iso8601 (this.sum (since, timeframePeriod * limit * 1000)); } const response = await this[method] (this.extend (request, params)); // // [ { market: "BTC-ETH", // candle_date_time_utc: "2018-11-22T13:47:00", // candle_date_time_kst: "2018-11-22T22:47:00", // opening_price: 0.02915963, // high_price: 0.02915963, // low_price: 0.02915448, // trade_price: 0.02915448, // timestamp: 1542894473674, // candle_acc_trade_price: 0.0981629437535248, // candle_acc_trade_volume: 3.36693173, // unit: 1 }, // { market: "BTC-ETH", // candle_date_time_utc: "2018-11-22T10:06:00", // candle_date_time_kst: "2018-11-22T19:06:00", // opening_price: 0.0294, // high_price: 0.02940882, // low_price: 0.02934283, // trade_price: 0.02937354, // timestamp: 1542881219276, // candle_acc_trade_price: 0.0762597110943884, // candle_acc_trade_volume: 2.5949617, // unit: 1 } ] // return this.parseOHLCVs (response, market, timeframe, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type === 'market') { // for market buy it requires the amount of quote currency to spend if (side === 'buy') { if (this.options['createMarketBuyOrderRequiresPrice']) { if (price === undefined) { throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); } else { amount = amount * price; } } } } let orderSide = undefined; if (side === 'buy') { orderSide = 'bid'; } else if (side === 'sell') { orderSide = 'ask'; } else { throw new InvalidOrder (this.id + ' createOrder allows buy or sell side only!'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'side': orderSide, }; if (type === 'limit') { request['volume'] = this.amountToPrecision (symbol, amount); request['price'] = this.priceToPrecision (symbol, price); request['ord_type'] = type; } else if (type === 'market') { if (side === 'buy') { request['ord_type'] = 'price'; request['price'] = this.priceToPrecision (symbol, amount); } else if (side === 'sell') { request['ord_type'] = type; request['volume'] = this.amountToPrecision (symbol, amount); } } const response = await this.privatePostOrders (this.extend (request, params)); // // { // 'uuid': 'cdd92199-2897-4e14-9448-f923320408ad', // 'side': 'bid', // 'ord_type': 'limit', // 'price': '100.0', // 'avg_price': '0.0', // 'state': 'wait', // 'market': 'KRW-BTC', // 'created_at': '2018-04-10T15:42:23+09:00', // 'volume': '0.01', // 'remaining_volume': '0.01', // 'reserved_fee': '0.0015', // 'remaining_fee': '0.0015', // 'paid_fee': '0.0', // 'locked': '1.0015', // 'executed_volume': '0.0', // 'trades_count': 0 // } // return this.parseOrder (response); } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'uuid': id, }; const response = await this.privateDeleteOrder (this.extend (request, params)); // // { // "uuid": "cdd92199-2897-4e14-9448-f923320408ad", // "side": "bid", // "ord_type": "limit", // "price": "100.0", // "state": "wait", // "market": "KRW-BTC", // "created_at": "2018-04-10T15:42:23+09:00", // "volume": "0.01", // "remaining_volume": "0.01", // "reserved_fee": "0.0015", // "remaining_fee": "0.0015", // "paid_fee": "0.0", // "locked": "1.0015", // "executed_volume": "0.0", // "trades_count": 0 // } // return this.parseOrder (response); } async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'page': 1, // 'order_by': 'asc', // 'desc' }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (limit !== undefined) { request['limit'] = limit; // default is 100 } const response = await this.privateGetDeposits (this.extend (request, params)); // // [ // { // "type": "deposit", // "uuid": "94332e99-3a87-4a35-ad98-28b0c969f830", // "currency": "KRW", // "txid": "9e37c537-6849-4c8b-a134-57313f5dfc5a", // "state": "ACCEPTED", // "created_at": "2017-12-08T15:38:02+09:00", // "done_at": "2017-12-08T15:38:02+09:00", // "amount": "100000.0", // "fee": "0.0" // }, // ..., // ] // return this.parseTransactions (response, currency, since, limit); } async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'state': 'submitting', // 'submitted', 'almost_accepted', 'rejected', 'accepted', 'processing', 'done', 'canceled' }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currency'] = currency['id']; } if (limit !== undefined) { request['limit'] = limit; // default is 100 } const response = await this.privateGetWithdraws (this.extend (request, params)); // // [ // { // "type": "withdraw", // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", // "currency": "BTC", // "txid": null, // "state": "processing", // "created_at": "2018-04-13T11:24:01+09:00", // "done_at": null, // "amount": "0.01", // "fee": "0.0", // "krw_amount": "80420.0" // }, // ..., // ] // return this.parseTransactions (response, currency, since, limit); } parseTransactionStatus (status) { const statuses = { 'ACCEPTED': 'ok', // deposits // withdrawals: 'submitting': 'pending', // 처리 중 'submitted': 'pending', // 처리 완료 'almost_accepted': 'pending', // 출금대기중 'rejected': 'failed', // 거부 'accepted': 'pending', // 승인됨 'processing': 'pending', // 처리 중 'done': 'ok', // 완료 'canceled': 'canceled', // 취소됨 }; return this.safeString (statuses, status, status); } parseTransaction (transaction, currency = undefined) { // // fetchDeposits // // { // "type": "deposit", // "uuid": "94332e99-3a87-4a35-ad98-28b0c969f830", // "currency": "KRW", // "txid": "9e37c537-6849-4c8b-a134-57313f5dfc5a", // "state": "ACCEPTED", // "created_at": "2017-12-08T15:38:02+09:00", // "done_at": "2017-12-08T15:38:02+09:00", // "amount": "100000.0", // "fee": "0.0" // } // // fetchWithdrawals // // { // "type": "withdraw", // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", // "currency": "BTC", // "txid": "cd81e9b45df8da29f936836e58c907a106057e454a45767a7b06fcb19b966bba", // "state": "processing", // "created_at": "2018-04-13T11:24:01+09:00", // "done_at": null, // "amount": "0.01", // "fee": "0.0", // "krw_amount": "80420.0" // } // const id = this.safeString (transaction, 'uuid'); const amount = this.safeFloat (transaction, 'amount'); const address = undefined; // not present in the data structure received from the exchange const tag = undefined; // not present in the data structure received from the exchange const txid = this.safeString (transaction, 'txid'); const updated = this.parse8601 (this.safeString (transaction, 'done_at')); const timestamp = this.parse8601 (this.safeString (transaction, 'created_at', updated)); let type = this.safeString (transaction, 'type'); if (type === 'withdraw') { type = 'withdrawal'; } const currencyId = this.safeString (transaction, 'currency'); const code = this.safeCurrencyCode (currencyId); const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); const feeCost = this.safeFloat (transaction, 'fee'); return { 'info': transaction, 'id': id, 'currency': code, 'amount': amount, 'address': address, 'tag': tag, 'status': status, 'type': type, 'updated': updated, 'txid': txid, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': { 'currency': code, 'cost': feeCost, }, }; } parseOrderStatus (status) { const statuses = { 'wait': 'open', 'done': 'closed', 'cancel': 'canceled', }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { // // { // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", // "side": "bid", // "ord_type": "limit", // "price": "17417000.0", // "state": "done", // "market": "KRW-BTC", // "created_at": "2018-04-05T14:09:14+09:00", // "volume": "1.0", // "remaining_volume": "0.0", // "reserved_fee": "26125.5", // "remaining_fee": "25974.0", // "paid_fee": "151.5", // "locked": "17341974.0", // "executed_volume": "1.0", // "trades_count": 2, // "trades": [ // { // "market": "KRW-BTC", // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", // "price": "101000.0", // "volume": "0.77368323", // "funds": "78142.00623", // "ask_fee": "117.213009345", // "bid_fee": "117.213009345", // "created_at": "2018-04-05T14:09:15+09:00", // "side": "bid", // }, // { // "market": "KRW-BTC", // "uuid": "f73da467-c42f-407d-92fa-e10d86450a20", // "price": "101000.0", // "volume": "0.22631677", // "funds": "22857.99377", // "ask_fee": "34.286990655", // missing in market orders // "bid_fee": "34.286990655", // missing in market orders // "created_at": "2018-04-05T14:09:15+09:00", // missing in market orders // "side": "bid", // }, // ], // } // const id = this.safeString (order, 'uuid'); let side = this.safeString (order, 'side'); if (side === 'bid') { side = 'buy'; } else { side = 'sell'; } let type = this.safeString (order, 'ord_type'); const timestamp = this.parse8601 (this.safeString (order, 'created_at')); const status = this.parseOrderStatus (this.safeString (order, 'state')); let lastTradeTimestamp = undefined; let price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'volume'); const remaining = this.safeFloat (order, 'remaining_volume'); const filled = this.safeFloat (order, 'executed_volume'); let cost = undefined; if (type === 'price') { type = 'market'; cost = price; price = undefined; } let average = undefined; let fee = undefined; let feeCost = this.safeFloat (order, 'paid_fee'); let feeCurrency = undefined; const marketId = this.safeString (order, 'market'); market = this.safeValue (this.markets_by_id, marketId); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; feeCurrency = market['quote']; } else { const [ baseId, quoteId ] = marketId.split ('-'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; feeCurrency = quote; } let trades = this.safeValue (order, 'trades', []); trades = this.parseTrades (trades, market, undefined, undefined, { 'order': id }); const numTrades = trades.length; if (numTrades > 0) { // the timestamp in fetchOrder trades is missing lastTradeTimestamp = trades[numTrades - 1]['timestamp']; let getFeesFromTrades = false; if (feeCost === undefined) { getFeesFromTrades = true; feeCost = 0; } cost = 0; for (let i = 0; i < numTrades; i++) { const trade = trades[i]; cost = this.sum (cost, trade['cost']); if (getFeesFromTrades) { const tradeFee = this.safeValue (trades[i], 'fee', {}); const tradeFeeCost = this.safeFloat (tradeFee, 'cost'); if (tradeFeeCost !== undefined) { feeCost = this.sum (feeCost, tradeFeeCost); } } } average = cost / filled; } if (feeCost !== undefined) { fee = { 'currency': feeCurrency, 'cost': feeCost, }; } const result = { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': cost, 'average': average, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': fee, 'trades': trades, }; return result; } async fetchOrdersByState (state, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = { // 'market': this.marketId (symbol), 'state': state, // 'page': 1, // 'order_by': 'asc', }; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); request['market'] = market['id']; } const response = await this.privateGetOrders (this.extend (request, params)); // // [ // { // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", // "side": "bid", // "ord_type": "limit", // "price": "17417000.0", // "state": "done", // "market": "KRW-BTC", // "created_at": "2018-04-05T14:09:14+09:00", // "volume": "1.0", // "remaining_volume": "0.0", // "reserved_fee": "26125.5", // "remaining_fee": "25974.0", // "paid_fee": "151.5", // "locked": "17341974.0", // "executed_volume": "1.0", // "trades_count":2 // }, // ] // return this.parseOrders (response, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState ('wait', symbol, since, limit, params); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState ('done', symbol, since, limit, params); } async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState ('cancel', symbol, since, limit, params); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'uuid': id, }; const response = await this.privateGetOrder (this.extend (request, params)); // // { // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", // "side": "bid", // "ord_type": "limit", // "price": "17417000.0", // "state": "done", // "market": "KRW-BTC", // "created_at": "2018-04-05T14:09:14+09:00", // "volume": "1.0", // "remaining_volume": "0.0", // "reserved_fee": "26125.5", // "remaining_fee": "25974.0", // "paid_fee": "151.5", // "locked": "17341974.0", // "executed_volume": "1.0", // "trades_count": 2, // "trades": [ // { // "market": "KRW-BTC", // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", // "price": "101000.0", // "volume": "0.77368323", // "funds": "78142.00623", // "ask_fee": "117.213009345", // "bid_fee": "117.213009345", // "created_at": "2018-04-05T14:09:15+09:00", // "side": "bid" // }, // { // "market": "KRW-BTC", // "uuid": "f73da467-c42f-407d-92fa-e10d86450a20", // "price": "101000.0", // "volume": "0.22631677", // "funds": "22857.99377", // "ask_fee": "34.286990655", // "bid_fee": "34.286990655", // "created_at": "2018-04-05T14:09:15+09:00", // "side": "bid" // } // ] // } // return this.parseOrder (response); } parseDepositAddresses (addresses) { const result = {}; for (let i = 0; i < addresses.length; i++) { const address = this.parseDepositAddress (addresses[i]); const code = address['currency']; result[code] = address; } return result; } async fetchDepositAddresses (codes = undefined, params = {}) { await this.loadMarkets (); const response = await this.privateGetDepositsCoinAddresses (params); // // [ // { // "currency": "BTC", // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", // "secondary_address": null // }, // { // "currency": "ETH", // "deposit_address": "0x0d73e0a482b8cf568976d2e8688f4a899d29301c", // "secondary_address": null // }, // { // "currency": "XRP", // "deposit_address": "rN9qNpgnBaZwqCg8CvUZRPqCcPPY7wfWep", // "secondary_address": "3057887915" // } // ] // return this.parseDepositAddresses (response); } parseDepositAddress (depositAddress, currency = undefined) { // // { // "currency": "BTC", // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", // "secondary_address": null // } // const address = this.safeString (depositAddress, 'deposit_address'); const tag = this.safeString (depositAddress, 'secondary_address'); const currencyId = this.safeString (depositAddress, 'currency'); const code = this.safeCurrencyCode (currencyId); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': tag, 'info': depositAddress, }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const response = await this.privateGetDepositsCoinAddress (this.extend ({ 'currency': currency['id'], }, params)); // // { // "currency": "BTC", // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", // "secondary_address": null // } // return this.parseDepositAddress (response); } async createDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; // https://github.com/ccxt/ccxt/issues/6452 const response = await this.privatePostDepositsGenerateCoinAddress (this.extend (request, params)); // // https://docs.upbit.com/v1.0/reference#%EC%9E%85%EA%B8%88-%EC%A3%BC%EC%86%8C-%EC%83%9D%EC%84%B1-%EC%9A%94%EC%B2%AD // can be any of the two responses: // // { // "success" : true, // "message" : "Creating BTC deposit address." // } // // { // "currency": "BTC", // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", // "secondary_address": null // } // const message = this.safeString (response, 'message'); if (message !== undefined) { throw new AddressPending (this.id + ' is generating ' + code + ' deposit address, call fetchDepositAddress or createDepositAddress one more time later to retrieve the generated address'); } return this.parseDepositAddress (response); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'amount': amount, }; let method = 'privatePostWithdraws'; if (code !== 'KRW') { method += 'Coin'; request['currency'] = currency['id']; request['address'] = address; if (tag !== undefined) { request['secondary_address'] = tag; } } else { method += 'Krw'; } const response = await this[method] (this.extend (request, params)); // // { // "type": "withdraw", // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", // "currency": "BTC", // "txid": "ebe6937b-130e-4066-8ac6-4b0e67f28adc", // "state": "processing", // "created_at": "2018-04-13T11:24:01+09:00", // "done_at": null, // "amount": "0.01", // "fee": "0.0", // "krw_amount": "80420.0" // } // return this.parseTransaction (response); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname, }); url += '/' + this.version + '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (method !== 'POST') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce (); const request = { 'access_key': this.apiKey, 'nonce': nonce, }; if (Object.keys (query).length) { const auth = this.urlencode (query); const hash = this.hash (this.encode (auth), 'sha512'); request['query_hash'] = hash; request['query_hash_alg'] = 'SHA512'; } const jwt = this.jwt (request, this.encode (this.secret)); headers = { 'Authorization': 'Bearer ' + jwt, }; if ((method !== 'GET') && (method !== 'DELETE')) { body = this.json (params); headers['Content-Type'] = 'application/json'; } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } // // { 'error': { 'message': "Missing request parameter error. Check the required parameters!", 'name': 400 } }, // { 'error': { 'message': "side is missing, side does not have a valid value", 'name': "validation_error" } }, // { 'error': { 'message': "개인정보 제 3자 제공 동의가 필요합니다.", 'name': "thirdparty_agreement_required" } }, // { 'error': { 'message': "권한이 부족합니다.", 'name': "out_of_scope" } }, // { 'error': { 'message': "주문을 찾지 못했습니다.", 'name': "order_not_found" } }, // { 'error': { 'message': "주문가능한 금액(ETH)이 부족합니다.", 'name': "insufficient_funds_ask" } }, // { 'error': { 'message': "주문가능한 금액(BTC)이 부족합니다.", 'name': "insufficient_funds_bid" } }, // { 'error': { 'message': "잘못된 엑세스 키입니다.", 'name': "invalid_access_key" } }, // { 'error': { 'message': "Jwt 토큰 검증에 실패했습니다.", 'name': "jwt_verification" } } // const error = this.safeValue (response, 'error'); if (error !== undefined) { const message = this.safeString (error, 'message'); const name = this.safeString (error, 'name'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], name, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], name, feedback); throw new ExchangeError (feedback); // unknown message } } }; },{"./base/Exchange":7,"./base/errors":9}],166:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); // --------------------------------------------------------------------------- module.exports = class vaultoro extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'vaultoro', 'name': 'Vaultoro', 'countries': [ 'CH' ], 'rateLimit': 1000, 'version': '1', 'has': { 'CORS': true, 'fetchMarkets': true, 'fetchOrderBook': true, 'fetchBalance': true, 'createOrder': true, 'cancelOrder': true, 'fetchTrades': true, 'fetchTicker': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766880-f205e870-5ee9-11e7-8fe2-0d5b15880752.jpg', 'api': 'https://api.vaultoro.com', 'www': 'https://www.vaultoro.com', 'doc': 'https://api.vaultoro.com', }, 'commonCurrencies': { 'GLD': 'Gold', }, 'api': { 'public': { 'get': [ 'bidandask', 'buyorders', 'latest', 'latesttrades', 'markets', 'orderbook', 'sellorders', 'transactions/day', 'transactions/hour', 'transactions/month', ], }, 'private': { 'get': [ 'balance', 'mytrades', 'orders', ], 'post': [ 'buy/{symbol}/{type}', 'cancel/{id}', 'sell/{symbol}/{type}', 'withdraw', ], }, }, }); } async fetchMarkets (params = {}) { const result = []; const response = await this.publicGetMarkets (params); const market = this.safeValue (response, 'data'); const baseId = this.safeString (market, 'MarketCurrency'); const quoteId = this.safeString (market, 'BaseCurrency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const id = this.safeString (market, 'MarketName'); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': undefined, 'precision': this.precision, 'limits': this.limits, }); return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetBalance (params); const balances = this.safeValue (response, 'data'); const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency_code'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (balance, 'cash'); account['used'] = this.safeFloat (balance, 'reserved'); result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetOrderbook (params); const orderbook = { 'bids': response['data'][0]['b'], 'asks': response['data'][1]['s'], }; return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'Gold_Price', 'Gold_Amount'); } parseTrade (trade, market = undefined) { const timestamp = this.parse8601 (this.safeString (trade, 'Time')); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const price = this.safeFloat (trade, 'Gold_Price'); const amount = this.safeFloat (trade, 'Gold_Amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } return { 'id': undefined, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'order': undefined, 'type': undefined, 'side': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const response = await this.publicGetTransactionsDay (params); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const method = 'privatePost' + this.capitalize (side) + 'SymbolType'; const request = { 'symbol': market['quoteId'].toLowerCase (), 'type': type, 'gld': amount, 'price': price || 1, }; const response = await this[method] (this.extend (request, params)); return { 'info': response, 'id': response['data']['Order_ID'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id, }; return await this.privatePostCancelId (this.extend (request, params)); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/'; if (api === 'public') { url += path; } else { this.checkRequiredCredentials (); const nonce = this.nonce (); url += this.version + '/' + this.implodeParams (path, params); const query = this.extend ({ 'nonce': nonce, 'apikey': this.apiKey, }, this.omit (params, this.extractParams (path))); url += '?' + this.urlencode (query); headers = { 'Content-Type': 'application/json', 'X-Signature': this.hmac (this.encode (url), this.encode (this.secret)), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7}],167:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const foxbit = require ('./foxbit.js'); // --------------------------------------------------------------------------- module.exports = class vbtc extends foxbit { describe () { return this.deepExtend (super.describe (), { 'id': 'vbtc', 'name': 'VBTC', 'countries': [ 'VN' ], 'has': { 'CORS': false, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27991481-1f53d1d8-6481-11e7-884e-21d17e7939db.jpg', 'api': { 'public': 'https://api.blinktrade.com/api', 'private': 'https://api.blinktrade.com/tapi', }, 'www': 'https://vbtc.exchange', 'doc': 'https://blinktrade.com/docs', }, 'options': { 'brokerId': '3', // https://blinktrade.com/docs/#brokers }, }); } }; },{"./foxbit.js":84}],168:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeNotAvailable, ExchangeError, DDoSProtection, BadSymbol } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class whitebit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'whitebit', 'name': 'WhiteBit', 'version': 'v2', 'countries': [ 'EE' ], 'rateLimit': 500, 'has': { 'cancelOrder': false, 'CORS': false, 'createDepositAddress': false, 'createLimitOrder': false, 'createMarketOrder': false, 'createOrder': false, 'deposit': false, 'editOrder': false, 'fetchBalance': false, 'fetchBidsAsks': false, 'fetchCurrencies': true, 'fetchMarkets': true, 'fetchOHLCV': true, 'fetchOrderBook': true, 'fetchStatus': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'privateAPI': false, 'publicAPI': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '8h': '8h', '12h': '12h', '1d': '1d', '3d': '3d', '1w': '1w', '1M': '1M', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/66732963-8eb7dd00-ee66-11e9-849b-10d9282bb9e0.jpg', 'api': { 'web': 'https://whitebit.com/', 'publicV2': 'https://whitebit.com/api/v2/public', 'publicV1': 'https://whitebit.com/api/v1/public', }, 'www': 'https://www.whitebit.com', 'doc': 'https://documenter.getpostman.com/view/7473075/SVSPomwS?version=latest#intro', 'fees': 'https://whitebit.com/fee-schedule', 'referral': 'https://whitebit.com/referral/d9bdf40e-28f2-4b52-b2f9-cd1415d82963', }, 'api': { 'web': { 'get': [ 'v1/healthcheck', ], }, 'publicV1': { 'get': [ 'markets', 'tickers', 'ticker', 'symbols', 'depth/result', 'history', 'kline', ], }, 'publicV2': { 'get': [ 'markets', 'ticker', 'assets', 'fee', 'depth/{market}', 'trades/{market}', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': 0.001, 'maker': 0.001, }, }, 'options': { 'fetchTradesMethod': 'fetchTradesV1', }, 'exceptions': { 'exact': { '503': ExchangeNotAvailable, // {"response":null,"status":503,"errors":{"message":[""]},"notification":null,"warning":null,"_token":null} }, 'broad': { 'Market is not available': BadSymbol, // {"success":false,"message":{"market":["Market is not available"]},"result":[]} }, }, }); } async fetchMarkets (params = {}) { const response = await this.publicV2GetMarkets (params); // // { // "success":true, // "message":"", // "result":[ // { // "name":"BTC_USD", // "moneyPrec":"2", // "stock":"BTC", // "money":"USD", // "stockPrec":"6", // "feePrec":"4", // "minAmount":"0.001", // "tradesEnabled":true, // "minTotal":"0.001" // } // ] // } // const markets = this.safeValue (response, 'result'); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'name'); const baseId = this.safeString (market, 'stock'); const quoteId = this.safeString (market, 'money'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const active = this.safeValue (market, 'tradesEnabled'); const entry = { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': active, 'precision': { 'amount': this.safeInteger (market, 'stockPrec'), 'price': this.safeInteger (market, 'moneyPrec'), }, 'limits': { 'amount': { 'min': this.safeFloat (market, 'minAmount'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeFloat (market, 'minTotal'), 'max': undefined, }, }, }; result.push (entry); } return result; } async fetchCurrencies (params = {}) { const response = await this.publicV2GetAssets (params); // // { // "success":true, // "message":"", // "result":{ // "BTC":{ // "id":"4f37bc79-f612-4a63-9a81-d37f7f9ff622", // "lastUpdateTimestamp":"2019-10-12T04:40:05.000Z", // "name":"Bitcoin", // "canWithdraw":true, // "canDeposit":true, // "minWithdrawal":"0.001", // "maxWithdrawal":"0", // "makerFee":"0.1", // "takerFee":"0.1" // } // } // } // const currencies = this.safeValue (response, 'result'); const ids = Object.keys (currencies); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const currency = currencies[id]; // breaks down in Python due to utf8 encoding issues on the exchange side // const name = this.safeString (currency, 'name'); const canDeposit = this.safeValue (currency, 'canDeposit', true); const canWithdraw = this.safeValue (currency, 'canWithdraw', true); const active = canDeposit && canWithdraw; const code = this.safeCurrencyCode (id); result[code] = { 'id': id, 'code': code, 'info': currency, // the original payload 'name': undefined, // see the comment above 'active': active, 'fee': undefined, 'precision': undefined, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': this.safeFloat (currency, 'minWithdrawal'), 'max': this.safeFloat (currency, 'maxWithdrawal'), }, }, }; } return result; } async fetchTradingFees (params = {}) { const response = await this.publicV2GetFee (params); const fees = this.safeValue (response, 'result'); return { 'maker': this.safeFloat (fees, 'makerFee'), 'taker': this.safeFloat (fees, 'takerFee'), }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; const response = await this.publicV1GetTicker (this.extend (request, params)); // // { // "success":true, // "message":"", // "result": { // "bid":"0.021979", // "ask":"0.021996", // "open":"0.02182", // "high":"0.022039", // "low":"0.02161", // "last":"0.021987", // "volume":"2810.267", // "deal":"61.383565474", // "change":"0.76", // }, // } // const ticker = this.safeValue (response, 'result', {}); return this.parseTicker (ticker, market); } parseTicker (ticker, market = undefined) { // // fetchTicker // // { // "bid":"0.021979", // "ask":"0.021996", // "open":"0.02182", // "high":"0.022039", // "low":"0.02161", // "last":"0.021987", // "volume":"2810.267", // "deal":"61.383565474", // "change":"0.76", // } // // fetchTickers v1 // // { // "at":1571022144, // "ticker": { // "bid":"0.022024", // "ask":"0.022042", // "low":"0.02161", // "high":"0.022062", // "last":"0.022036", // "vol":"2813.503", // "deal":"61.457279261", // "change":"0.95" // } // } // const timestamp = this.safeTimestamp (ticker, 'at', this.milliseconds ()); ticker = this.safeValue (ticker, 'ticker', ticker); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); const percentage = this.safeFloat (ticker, 'change'); let change = undefined; if (percentage !== undefined) { change = this.numberToString (percentage * 0.01); } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': this.safeFloat (ticker, 'open'), 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'volume'), 'quoteVolume': this.safeFloat (ticker, 'deal'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicV1GetTickers (params); // // { // "success":true, // "message":"", // "result": { // "ETH_BTC": { // "at":1571022144, // "ticker": { // "bid":"0.022024", // "ask":"0.022042", // "low":"0.02161", // "high":"0.022062", // "last":"0.022036", // "vol":"2813.503", // "deal":"61.457279261", // "change":"0.95" // } // }, // }, // } // const data = this.safeValue (response, 'result'); const marketIds = Object.keys (data); const result = {}; for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; let market = undefined; let symbol = marketId; if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; symbol = market['symbol']; } else { const [ baseId, quoteId ] = marketId.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } const ticker = this.parseTicker (data[marketId], market); result[symbol] = this.extend (ticker, { 'symbol': symbol }); } return this.filterByArray (result, 'symbol', symbols); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = 50, maximum = 100 } const response = await this.publicV2GetDepthMarket (this.extend (request, params)); // // { // "success":true, // "message":"", // "result":{ // "lastUpdateTimestamp":"2019-10-14T03:15:47.000Z", // "asks":[ // ["0.02204","2.03"], // ["0.022041","2.492"], // ["0.022042","2.254"], // ], // "bids":[ // ["0.022018","2.327"], // ["0.022017","1.336"], // ["0.022015","2.089"], // ], // } // } // const result = this.safeValue (response, 'result', {}); const timestamp = this.parse8601 (this.safeString (result, 'lastUpdateTimestamp')); return this.parseOrderBook (result, timestamp); } async fetchTradesV1 (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'lastId': 1, // todo add since }; if (limit !== undefined) { request['limit'] = limit; // default = 50, maximum = 10000 } const response = await this.publicV1GetHistory (this.extend (request, params)); // // { // "success":true, // "message":"", // "result":[ // { // "id":11887426, // "type":"buy", // "time":1571023057.413769, // "amount":"0.171", // "price":"0.022052" // } // ], // } // const result = this.safeValue (response, 'result', []); return this.parseTrades (result, market, since, limit); } async fetchTradesV2 (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = 50, maximum = 10000 } const response = await this.publicV2GetTradesMarket (this.extend (request, params)); // // { // "success":true, // "message":"", // "result": [ // { // "tradeId":11903347, // "price":"0.022044", // "volume":"0.029", // "time":"2019-10-14T06:30:57.000Z", // "isBuyerMaker":false // }, // ], // } // const result = this.safeValue (response, 'result', []); return this.parseTrades (result, market, since, limit); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { const method = this.safeString (this.options, 'fetchTradesMethod', 'fetchTradesV2'); return await this[method] (symbol, since, limit, params); } parseTrade (trade, market = undefined) { // // fetchTradesV1 // // { // "id":11887426, // "type":"buy", // "time":1571023057.413769, // "amount":"0.171", // "price":"0.022052" // } // // fetchTradesV2 // // { // "tradeId":11903347, // "price":"0.022044", // "volume":"0.029", // "time":"2019-10-14T06:30:57.000Z", // "isBuyerMaker":false // } // let timestamp = this.safeValue (trade, 'time'); if (typeof timestamp === 'string') { timestamp = this.parse8601 (timestamp); } else { timestamp = parseInt (timestamp * 1000); } const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat2 (trade, 'amount', 'volume'); const id = this.safeString2 (trade, 'id', 'tradeId'); let side = this.safeString (trade, 'type'); if (side === undefined) { const isBuyerMaker = this.safeValue (trade, 'isBuyerMaker'); side = isBuyerMaker ? 'buy' : 'sell'; } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } let cost = undefined; if (amount !== undefined && price !== undefined) { cost = amount * price; } return { 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'id': id, 'order': undefined, 'type': undefined, 'takerOrMaker': undefined, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'market': market['id'], 'interval': this.timeframes[timeframe], }; if (since !== undefined) { request['start'] = parseInt (since / 1000); } if (limit !== undefined) { request['limit'] = limit; // default == max == 500 } const response = await this.publicV1GetKline (this.extend (request, params)); const result = this.safeValue (response, 'result'); return this.parseOHLCVs (result, market, timeframe, since, limit); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv[0] * 1000, // timestamp parseFloat (ohlcv[1]), // open parseFloat (ohlcv[3]), // high parseFloat (ohlcv[4]), // low parseFloat (ohlcv[2]), // close parseFloat (ohlcv[5]), // volume ]; } async fetchStatus (params = {}) { const response = await this.webGetV1Healthcheck (params); const status = this.safeInteger (response, 'status'); let formattedStatus = 'ok'; if (status === 503) { formattedStatus = 'maintenance'; } this.status = this.extend (this.status, { 'status': formattedStatus, 'updated': this.milliseconds (), }); return this.status; } sign (path, api = 'publicV1', method = 'GET', params = {}, headers = undefined, body = undefined) { const query = this.omit (params, this.extractParams (path)); let url = this.urls['api'][api] + '/' + this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if ((code === 418) || (code === 429)) { throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); } if (code === 404) { throw new ExchangeError (this.id + ' ' + code.toString () + ' endpoint not found'); } if (response !== undefined) { const success = this.safeValue (response, 'success'); if (!success) { const feedback = this.id + ' ' + body; const status = this.safeString (response, 'status'); if (typeof status === 'string') { this.throwExactlyMatchedException (this.exceptions['exact'], status, feedback); } this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); throw new ExchangeError (feedback); } } } }; },{"./base/Exchange":7,"./base/errors":9}],169:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, NotSupported, AuthenticationError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class xbtce extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'xbtce', 'name': 'xBTCe', 'countries': [ 'RU' ], 'rateLimit': 2000, // responses are cached every 2 seconds 'version': 'v1', 'has': { 'CORS': false, 'fetchTickers': true, 'createMarketOrder': false, 'fetchOHLCV': false, }, 'urls': { 'referral': 'https://xbtce.com/?agent=XX97BTCXXXG687021000B', 'logo': 'https://user-images.githubusercontent.com/1294454/28059414-e235970c-662c-11e7-8c3a-08e31f78684b.jpg', 'api': 'https://cryptottlivewebapi.xbtce.net:8443/api', 'www': 'https://www.xbtce.com', 'doc': [ 'https://www.xbtce.com/tradeapi', 'https://support.xbtce.info/Knowledgebase/Article/View/52/25/xbtce-exchange-api', ], }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'uid': true, }, 'api': { 'public': { 'get': [ 'currency', 'currency/{filter}', 'level2', 'level2/{filter}', 'quotehistory/{symbol}/{periodicity}/bars/ask', 'quotehistory/{symbol}/{periodicity}/bars/bid', 'quotehistory/{symbol}/level2', 'quotehistory/{symbol}/ticks', 'symbol', 'symbol/{filter}', 'tick', 'tick/{filter}', 'ticker', 'ticker/{filter}', 'tradesession', ], }, 'private': { 'get': [ 'tradeserverinfo', 'tradesession', 'currency', 'currency/{filter}', 'level2', 'level2/{filter}', 'symbol', 'symbol/{filter}', 'tick', 'tick/{filter}', 'account', 'asset', 'asset/{id}', 'position', 'position/{id}', 'trade', 'trade/{id}', 'quotehistory/{symbol}/{periodicity}/bars/ask', 'quotehistory/{symbol}/{periodicity}/bars/ask/info', 'quotehistory/{symbol}/{periodicity}/bars/bid', 'quotehistory/{symbol}/{periodicity}/bars/bid/info', 'quotehistory/{symbol}/level2', 'quotehistory/{symbol}/level2/info', 'quotehistory/{symbol}/periodicities', 'quotehistory/{symbol}/ticks', 'quotehistory/{symbol}/ticks/info', 'quotehistory/cache/{symbol}/{periodicity}/bars/ask', 'quotehistory/cache/{symbol}/{periodicity}/bars/bid', 'quotehistory/cache/{symbol}/level2', 'quotehistory/cache/{symbol}/ticks', 'quotehistory/symbols', 'quotehistory/version', ], 'post': [ 'trade', 'tradehistory', ], 'put': [ 'trade', ], 'delete': [ 'trade', ], }, }, 'commonCurrencies': { 'DSH': 'DASH', }, }); } async fetchMarkets (params = {}) { const response = await this.privateGetSymbol (params); const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString (market, 'Symbol'); const baseId = this.safeString (market, 'MarginCurrency'); const quoteId = this.safeString (market, 'ProfitCurrency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); let symbol = base + '/' + quote; symbol = market['IsTradeAllowed'] ? symbol : id; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'active': undefined, 'precision': this.precision, 'limits': this.limits, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const balances = await this.privateGetAsset (params); const result = { 'info': balances }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'Currency'); const code = this.safeCurrencyCode (currencyId); const account = { 'free': this.safeFloat (balance, 'FreeAmount'), 'used': this.safeFloat (balance, 'LockedAmount'), 'total': this.safeFloat (balance, 'Amount'), }; result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'filter': market['id'], }; const response = await this.privateGetLevel2Filter (this.extend (request, params)); const orderbook = response[0]; const timestamp = this.safeInteger (orderbook, 'Timestamp'); return this.parseOrderBook (orderbook, timestamp, 'Bids', 'Asks', 'Price', 'Volume'); } parseTicker (ticker, market = undefined) { let timestamp = 0; let last = undefined; if ('LastBuyTimestamp' in ticker) { if (timestamp < ticker['LastBuyTimestamp']) { timestamp = ticker['LastBuyTimestamp']; last = ticker['LastBuyPrice']; } } if ('LastSellTimestamp' in ticker) { if (timestamp < ticker['LastSellTimestamp']) { timestamp = ticker['LastSellTimestamp']; last = ticker['LastSellPrice']; } } if (!timestamp) { timestamp = this.milliseconds (); } let symbol = undefined; if (market) { symbol = market['symbol']; } return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': ticker['DailyBestBuyPrice'], 'low': ticker['DailyBestSellPrice'], 'bid': ticker['BestBid'], 'bidVolume': undefined, 'ask': ticker['BestAsk'], 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': ticker['DailyTradedTotalVolume'], 'quoteVolume': undefined, 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetTicker (params); const tickers = this.indexBy (response, 'Symbol'); const ids = Object.keys (tickers); const result = {}; for (let i = 0; i < ids.length; i++) { const id = ids[i]; let market = undefined; let symbol = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } else { const baseId = id.slice (0, 3); const quoteId = id.slice (3, 6); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } const ticker = tickers[id]; result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'filter': market['id'], }; const response = await this.publicGetTickerFilter (this.extend (request, params)); const length = response.length; if (length < 1) { throw new ExchangeError (this.id + ' fetchTicker returned empty response, xBTCe public API error'); } const tickers = this.indexBy (response, 'Symbol'); const ticker = tickers[market['id']]; return this.parseTicker (ticker, market); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); // no method for trades? return await this.privateGetTrade (params); } parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { return [ ohlcv['Timestamp'], ohlcv['Open'], ohlcv['High'], ohlcv['Low'], ohlcv['Close'], ohlcv['Volume'], ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { // let minutes = parseInt (timeframe / 60); // 1 minute by default // let periodicity = minutes.toString (); // await this.loadMarkets (); // let market = this.market (symbol); // if (since === undefined) // since = this.seconds () - 86400 * 7; // last day by defulat // if (limit === undefined) // limit = 1000; // default // let response = await this.privateGetQuotehistorySymbolPeriodicityBarsBid (this.extend ({ // 'symbol': market['id'], // 'periodicity': periodicity, // 'timestamp': since, // 'count': limit, // }, params)); // return this.parseOHLCVs (response['Bars'], market, timeframe, since, limit); throw new NotSupported (this.id + ' fetchOHLCV is disabled by the exchange'); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } const request = { 'pair': this.marketId (symbol), 'type': side, 'amount': amount, 'rate': price, }; const response = await this.privatePostTrade (this.extend (request, params)); return { 'info': response, 'id': response['Id'].toString (), }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'Type': 'Cancel', 'Id': id, }; return await this.privateDeleteTrade (this.extend (request, params)); } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { if (!this.apiKey) { throw new AuthenticationError (this.id + ' requires apiKey for all requests, their public API is always busy'); } if (!this.uid) { throw new AuthenticationError (this.id + ' requires uid property for authentication and trading, their public API is always busy'); } let url = this.urls['api'] + '/' + this.version; if (api === 'public') { url += '/' + api; } url += '/' + this.implodeParams (path, params); const query = this.omit (params, this.extractParams (path)); if (api === 'public') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { this.checkRequiredCredentials (); headers = { 'Accept-Encoding': 'gzip, deflate' }; const nonce = this.nonce ().toString (); if (method === 'POST') { if (Object.keys (query).length) { headers['Content-Type'] = 'application/json'; body = this.json (query); } else { url += '?' + this.urlencode (query); } } let auth = nonce + this.uid + this.apiKey + method + url; if (body) { auth += body; } const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); const credentials = this.uid + ':' + this.apiKey + ':' + nonce + ':' + this.decode (signature); headers['Authorization'] = 'HMAC ' + credentials; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } }; },{"./base/Exchange":7,"./base/errors":9}],170:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InvalidNonce, InsufficientFunds, OrderNotFound, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class yobit extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'yobit', 'name': 'YoBit', 'countries': [ 'RU' ], 'rateLimit': 3000, // responses are cached every 2 seconds 'version': '3', 'has': { 'CORS': false, 'createDepositAddress': true, 'createMarketOrder': false, 'fetchClosedOrders': 'emulated', 'fetchDepositAddress': true, 'fetchDeposits': false, 'fetchMyTrades': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBooks': true, 'fetchOrders': 'emulated', 'fetchTickers': true, 'fetchTransactions': false, 'fetchWithdrawals': false, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766910-cdcbfdae-5eea-11e7-9859-03fea873272d.jpg', 'api': { 'public': 'https://yobit.net/api', 'private': 'https://yobit.net/tapi', }, 'www': 'https://www.yobit.net', 'doc': 'https://www.yobit.net/en/api/', 'fees': 'https://www.yobit.net/en/fees/', }, 'api': { 'public': { 'get': [ 'depth/{pair}', 'info', 'ticker/{pair}', 'trades/{pair}', ], }, 'private': { 'post': [ 'ActiveOrders', 'CancelOrder', 'GetDepositAddress', 'getInfo', 'OrderInfo', 'Trade', 'TradeHistory', 'WithdrawCoinsToAddress', ], }, }, 'fees': { 'trading': { 'maker': 0.002, 'taker': 0.002, }, 'funding': { 'withdraw': {}, }, }, 'commonCurrencies': { 'AIR': 'AirCoin', 'ANI': 'ANICoin', 'ANT': 'AntsCoin', // what is this, a coin for ants? 'ATMCHA': 'ATM', 'ASN': 'Ascension', 'AST': 'Astral', 'ATM': 'Autumncoin', 'BCC': 'BCH', 'BCS': 'BitcoinStake', 'BLN': 'Bulleon', 'BOT': 'BOTcoin', 'BON': 'BONES', 'BPC': 'BitcoinPremium', 'BTS': 'Bitshares2', 'CAT': 'BitClave', 'CMT': 'CometCoin', 'COV': 'Coven Coin', 'COVX': 'COV', 'CPC': 'Capricoin', 'CS': 'CryptoSpots', 'DCT': 'Discount', 'DFT': 'DraftCoin', 'DGD': 'DarkGoldCoin', 'DIRT': 'DIRTY', 'DROP': 'FaucetCoin', 'DSH': 'DASH', 'EKO': 'EkoCoin', 'ENTER': 'ENTRC', 'EPC': 'ExperienceCoin', 'ESC': 'EdwardSnowden', 'EUROPE': 'EUROP', 'EXT': 'LifeExtension', 'FUNK': 'FUNKCoin', 'GCC': 'GlobalCryptocurrency', 'GEN': 'Genstake', 'GENE': 'Genesiscoin', 'GOLD': 'GoldMint', 'GOT': 'Giotto Coin', 'HTML5': 'HTML', 'HYPERX': 'HYPER', 'ICN': 'iCoin', 'INSANE': 'INSN', 'JNT': 'JointCoin', 'JPC': 'JupiterCoin', 'KNC': 'KingN Coin', 'LBTCX': 'LiteBitcoin', 'LIZI': 'LiZi', 'LOC': 'LocoCoin', 'LOCX': 'LOC', 'LUNYR': 'LUN', 'LUN': 'LunarCoin', // they just change the ticker if it is already taken 'MDT': 'Midnight', 'NAV': 'NavajoCoin', 'NBT': 'NiceBytes', 'OMG': 'OMGame', 'PAC': '$PAC', 'PLAY': 'PlayCoin', 'PIVX': 'Darknet', 'PRS': 'PRE', 'PUTIN': 'PUT', 'STK': 'StakeCoin', 'SUB': 'Subscriptio', 'PAY': 'EPAY', 'PLC': 'Platin Coin', 'RCN': 'RCoin', 'REP': 'Republicoin', 'RUR': 'RUB', 'TTC': 'TittieCoin', 'XIN': 'XINCoin', }, 'options': { // 'fetchTickersMaxLength': 2048, 'fetchOrdersRequiresSymbol': true, 'fetchTickersMaxLength': 512, }, 'exceptions': { 'exact': { '803': InvalidOrder, // "Count could not be less than 0.001." (selling below minAmount) '804': InvalidOrder, // "Count could not be more than 10000." (buying above maxAmount) '805': InvalidOrder, // "price could not be less than X." (minPrice violation on buy & sell) '806': InvalidOrder, // "price could not be more than X." (maxPrice violation on buy & sell) '807': InvalidOrder, // "cost could not be less than X." (minCost violation on buy & sell) '831': InsufficientFunds, // "Not enougth X to create buy order." (buying with balance.quote < order.cost) '832': InsufficientFunds, // "Not enougth X to create sell order." (selling with balance.base < order.amount) '833': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) }, 'broad': { 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} 'invalid api key': AuthenticationError, 'invalid sign': AuthenticationError, 'api key dont have trade permission': AuthenticationError, 'invalid parameter': InvalidOrder, 'invalid order': InvalidOrder, 'The given order has already been cancelled': InvalidOrder, 'Requests too often': DDoSProtection, 'not available': ExchangeNotAvailable, 'data unavailable': ExchangeNotAvailable, 'external service unavailable': ExchangeNotAvailable, 'Total transaction amount': InvalidOrder, // { "success": 0, "error": "Total transaction amount is less than minimal total: 0.00010000"} 'Insufficient funds': InsufficientFunds, 'invalid key': AuthenticationError, 'invalid nonce': InvalidNonce, // {"success":0,"error":"invalid nonce (has already been used)"}' }, }, }); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGetInfo (params); // // { // "success":1, // "return":{ // "funds":{ // "ltc":22, // "nvc":423.998, // "ppc":10, // }, // "funds_incl_orders":{ // "ltc":32, // "nvc":523.998, // "ppc":20, // }, // "rights":{ // "info":1, // "trade":0, // "withdraw":0 // }, // "transaction_count":0, // "open_orders":1, // "server_time":1418654530 // } // } // const balances = this.safeValue (response, 'return', {}); const result = { 'info': response }; const free = this.safeValue (balances, 'funds', {}); const total = this.safeValue (balances, 'funds_incl_orders', {}); const currencyIds = Object.keys (this.extend (free, total)); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeFloat (free, currencyId); account['total'] = this.safeFloat (total, currencyId); result[code] = account; } return this.parseBalance (result); } async fetchMarkets (params = {}) { const response = await this.publicGetInfo (params); const markets = this.safeValue (response, 'pairs'); const keys = Object.keys (markets); const result = []; for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = markets[id]; const [ baseId, quoteId ] = id.split ('_'); let base = baseId.toUpperCase (); let quote = quoteId.toUpperCase (); base = this.safeCurrencyCode (base); quote = this.safeCurrencyCode (quote); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'decimal_places'), 'price': this.safeInteger (market, 'decimal_places'), }; const amountLimits = { 'min': this.safeFloat (market, 'min_amount'), 'max': this.safeFloat (market, 'max_amount'), }; const priceLimits = { 'min': this.safeFloat (market, 'min_price'), 'max': this.safeFloat (market, 'max_price'), }; const costLimits = { 'min': this.safeFloat (market, 'min_total'), }; const limits = { 'amount': amountLimits, 'price': priceLimits, 'cost': costLimits, }; const hidden = this.safeInteger (market, 'hidden'); const active = (hidden === 0); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': active, 'taker': market['fee'] / 100, 'precision': precision, 'limits': limits, 'info': market, }); } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['limit'] = limit; // default = 150, max = 2000 } const response = await this.publicGetDepthPair (this.extend (request, params)); const market_id_in_reponse = (market['id'] in response); if (!market_id_in_reponse) { throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); } const orderbook = response[market['id']]; return this.parseOrderBook (orderbook); } async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let ids = undefined; if (symbols === undefined) { ids = this.ids.join ('-'); // max URL length is 2083 symbols, including http schema, hostname, tld, etc... if (ids.length > 2048) { const numIds = this.ids.length; throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); } } else { ids = this.marketIds (symbols); ids = ids.join ('-'); } const request = { 'pair': ids, // 'ignore_invalid': true, }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetDepthPair (this.extend (request, params)); const result = {}; ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; let symbol = id; if (id in this.markets_by_id) { const market = this.markets_by_id[id]; symbol = market['symbol']; } result[symbol] = this.parseOrderBook (response[id]); } return result; } parseTicker (ticker, market = undefined) { // // { high: 0.03497582, // low: 0.03248474, // avg: 0.03373028, // vol: 120.11485715062999, // vol_cur: 3572.24914074, // last: 0.0337611, // buy: 0.0337442, // sell: 0.03377798, // updated: 1537522009 } // const timestamp = this.safeTimestamp (ticker, 'updated'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeFloat (ticker, 'avg'), 'baseVolume': this.safeFloat (ticker, 'vol_cur'), 'quoteVolume': this.safeFloat (ticker, 'vol'), 'info': ticker, }; } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); let ids = this.ids; if (symbols === undefined) { const numIds = ids.length; ids = ids.join ('-'); const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); // max URL length is 2048 symbols, including http schema, hostname, tld, etc... if (ids.length > this.options['fetchTickersMaxLength']) { throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); } } else { ids = this.marketIds (symbols); ids = ids.join ('-'); } const request = { 'pair': ids, }; const tickers = await this.publicGetTickerPair (this.extend (request, params)); const result = {}; const keys = Object.keys (tickers); for (let k = 0; k < keys.length; k++) { const id = keys[k]; const ticker = tickers[id]; let symbol = id; let market = undefined; if (id in this.markets_by_id) { market = this.markets_by_id[id]; symbol = market['symbol']; } result[symbol] = this.parseTicker (ticker, market); } return result; } async fetchTicker (symbol, params = {}) { const tickers = await this.fetchTickers ([ symbol ], params); return tickers[symbol]; } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'timestamp'); let side = this.safeString (trade, 'type'); if (side === 'ask') { side = 'sell'; } else if (side === 'bid') { side = 'buy'; } const price = this.safeFloat2 (trade, 'rate', 'price'); const id = this.safeString2 (trade, 'trade_id', 'tid'); const order = this.safeString (trade, 'order_id'); if ('pair' in trade) { const marketId = this.safeString (trade, 'pair'); market = this.safeValue (this.markets_by_id, marketId, market); } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const amount = this.safeFloat (trade, 'amount'); const type = 'limit'; // all trades are still limit trades let takerOrMaker = undefined; let fee = undefined; const feeCost = this.safeFloat (trade, 'commission'); if (feeCost !== undefined) { const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } const isYourOrder = this.safeValue (trade, 'is_your_order'); if (isYourOrder !== undefined) { takerOrMaker = 'taker'; if (isYourOrder) { takerOrMaker = 'maker'; } if (fee === undefined) { fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); } } let cost = undefined; if (amount !== undefined) { if (price !== undefined) { cost = amount * price; } } return { 'id': id, 'order': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': fee, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.publicGetTradesPair (this.extend (request, params)); if (Array.isArray (response)) { const numElements = response.length; if (numElements === 0) { return []; } } return this.parseTrades (response[market['id']], market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type === 'market') { throw new ExchangeError (this.id + ' allows limit orders only'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], 'type': side, 'amount': this.amountToPrecision (symbol, amount), 'rate': this.priceToPrecision (symbol, price), }; price = parseFloat (price); amount = parseFloat (amount); const response = await this.privatePostTrade (this.extend (request, params)); let id = undefined; let status = 'open'; let filled = 0.0; let remaining = amount; if ('return' in response) { id = this.safeString (response['return'], 'order_id'); if (id === '0') { id = this.safeString (response['return'], 'init_order_id'); status = 'closed'; } filled = this.safeFloat (response['return'], 'received', 0.0); remaining = this.safeFloat (response['return'], 'remains', amount); } const timestamp = this.milliseconds (); const order = { 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'cost': price * filled, 'amount': amount, 'remaining': remaining, 'filled': filled, 'fee': undefined, // 'trades': this.parseTrades (order['trades'], market), 'info': response, 'clientOrderId': undefined, 'average': undefined, 'trades': undefined, }; this.orders[id] = order; return order; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': parseInt (id), }; const response = await this.privatePostCancelOrder (this.extend (request, params)); if (id in this.orders) { this.orders[id]['status'] = 'canceled'; } return response; } parseOrderStatus (status) { const statuses = { '0': 'open', '1': 'closed', '2': 'canceled', '3': 'open', // or partially-filled and canceled? https://github.com/ccxt/ccxt/issues/1594 }; return this.safeString (statuses, status, status); } parseOrder (order, market = undefined) { const id = this.safeString (order, 'id'); const status = this.parseOrderStatus (this.safeString (order, 'status')); const timestamp = this.safeTimestamp (order, 'timestamp_created'); let symbol = undefined; if (market === undefined) { const marketId = this.safeString (order, 'pair'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } if (market !== undefined) { symbol = market['symbol']; } const remaining = this.safeFloat (order, 'amount'); let amount = undefined; const price = this.safeFloat (order, 'rate'); let filled = undefined; let cost = undefined; if ('start_amount' in order) { amount = this.safeFloat (order, 'start_amount'); } else { if (id in this.orders) { amount = this.orders[id]['amount']; } } if (amount !== undefined) { if (remaining !== undefined) { filled = amount - remaining; cost = price * filled; } } const fee = undefined; const type = 'limit'; const side = this.safeString (order, 'type'); const result = { 'info': order, 'id': id, 'clientOrderId': undefined, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'type': type, 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'remaining': remaining, 'filled': filled, 'status': status, 'fee': fee, 'average': undefined, 'trades': undefined, }; return result; } parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { const result = []; const ids = Object.keys (orders); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } for (let i = 0; i < ids.length; i++) { const id = ids[i]; const order = this.extend ({ 'id': id }, orders[id]); result.push (this.extend (this.parseOrder (order, market), params)); } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } async fetchOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'order_id': parseInt (id), }; const response = await this.privatePostOrderInfo (this.extend (request, params)); id = id.toString (); const newOrder = this.parseOrder (this.extend ({ 'id': id }, response['return'][id])); const oldOrder = (id in this.orders) ? this.orders[id] : {}; this.orders[id] = this.extend (oldOrder, newOrder); return this.orders[id]; } updateCachedOrders (openOrders, symbol) { // update local cache with open orders // this will add unseen orders and overwrite existing ones for (let j = 0; j < openOrders.length; j++) { const id = openOrders[j]['id']; this.orders[id] = openOrders[j]; } const openOrdersIndexedById = this.indexBy (openOrders, 'id'); const cachedOrderIds = Object.keys (this.orders); for (let k = 0; k < cachedOrderIds.length; k++) { // match each cached order to an order in the open orders array // possible reasons why a cached order may be missing in the open orders array: // - order was closed or canceled -> update cache // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip const cachedOrderId = cachedOrderIds[k]; let cachedOrder = this.orders[cachedOrderId]; if (!(cachedOrderId in openOrdersIndexedById)) { // cached order is not in open orders array // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order if (symbol !== undefined && symbol !== cachedOrder['symbol']) { continue; } // cached order is absent from the list of open orders -> mark the cached order as closed if (cachedOrder['status'] === 'open') { cachedOrder = this.extend (cachedOrder, { 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") 'cost': undefined, 'filled': cachedOrder['amount'], 'remaining': 0.0, }); if (cachedOrder['cost'] === undefined) { if (cachedOrder['filled'] !== undefined) { cachedOrder['cost'] = cachedOrder['filled'] * cachedOrder['price']; } } this.orders[cachedOrderId] = cachedOrder; } } } return this.toArray (this.orders); } async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); } await this.loadMarkets (); const request = {}; const market = undefined; if (symbol !== undefined) { const market = this.market (symbol); request['pair'] = market['id']; } const response = await this.privatePostActiveOrders (this.extend (request, params)); // can only return 'open' orders (i.e. no way to fetch 'closed' orders) let openOrders = []; if ('return' in response) { openOrders = this.parseOrders (response['return'], market); } const allOrders = this.updateCachedOrders (openOrders, symbol); const result = this.filterBySymbol (allOrders, symbol); return this.filterBySinceLimit (result, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'open'); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { const orders = await this.fetchOrders (symbol, since, limit, params); return this.filterBy (orders, 'status', 'closed'); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); } await this.loadMarkets (); const market = this.market (symbol); // some derived classes use camelcase notation for request fields const request = { // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) // 'count': 1000, // the number of trades for display numerical, default = 1000 // 'from_id': trade ID, from which the display starts numerical 0 // 'end_id': trade ID on which the display ends numerical ∞ // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) 'pair': market['id'], }; if (limit !== undefined) { request['count'] = parseInt (limit); } if (since !== undefined) { request['since'] = parseInt (since / 1000); } const response = await this.privatePostTradeHistory (this.extend (request, params)); const trades = this.safeValue (response, 'return', {}); const ids = Object.keys (trades); const result = []; for (let i = 0; i < ids.length; i++) { const id = ids[i]; const trade = this.parseTrade (this.extend (trades[id], { 'trade_id': id, }), market); result.push (trade); } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } async createDepositAddress (code, params = {}) { const request = { 'need_new': 1, }; const response = await this.fetchDepositAddress (code, this.extend (request, params)); const address = this.safeString (response, 'address'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': undefined, 'info': response['info'], }; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'coinName': currency['id'], 'need_new': 0, }; const response = await this.privatePostGetDepositAddress (this.extend (request, params)); const address = this.safeString (response['return'], 'address'); this.checkAddress (address); return { 'currency': code, 'address': address, 'tag': undefined, 'info': response, }; } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); const request = { 'coinName': currency['id'], 'amount': amount, 'address': address, }; // no docs on the tag, yet... if (tag !== undefined) { throw new ExchangeError (this.id + ' withdraw() does not support the tag argument yet due to a lack of docs on withdrawing with tag/memo on behalf of the exchange.'); } const response = await this.privatePostWithdrawCoinsToAddress (this.extend (request, params)); return { 'info': response, 'id': undefined, }; } calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { const market = this.markets[symbol]; let key = 'quote'; const rate = market[takerOrMaker]; let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); if (side === 'sell') { cost *= price; } else { key = 'base'; } return { 'type': takerOrMaker, 'currency': market[key], 'rate': rate, 'cost': cost, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; const query = this.omit (params, this.extractParams (path)); if (api === 'private') { this.checkRequiredCredentials (); const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'nonce': nonce, 'method': path, }, query)); const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': signature, }; } else if (api === 'public') { url += '/' + this.version + '/' + this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { url += '/' + this.implodeParams (path, params); if (method === 'GET') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else { if (Object.keys (query).length) { body = this.json (query); headers = { 'Content-Type': 'application/json', }; } } } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if ('success' in response) { // // 1 - Liqui only returns the integer 'success' key from their private API // // { "success": 1, ... } httpCode === 200 // { "success": 0, ... } httpCode === 200 // // 2 - However, exchanges derived from Liqui, can return non-integers // // It can be a numeric string // { "sucesss": "1", ... } // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) // // Or just a string // { "success": "true", ... } // { "success": "false", ... }, httpCode >= 200 // // Or a boolean // { "success": true, ... } // { "success": false, ... }, httpCode >= 200 // // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types // // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui // // To cover points 1, 2, 3 and 4 combined this handler should work like this: // let success = this.safeValue (response, 'success', false); if (typeof success === 'string') { if ((success === 'true') || (success === '1')) { success = true; } else { success = false; } } if (!success) { const code = this.safeString (response, 'code'); const message = this.safeString (response, 'error'); const feedback = this.id + ' ' + body; this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); throw new ExchangeError (feedback); // unknown message } } } }; },{"./base/Exchange":7,"./base/errors":9}],171:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, BadRequest } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class zaif extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'zaif', 'name': 'Zaif', 'countries': [ 'JP' ], 'rateLimit': 2000, 'version': '1', 'has': { 'CORS': false, 'createMarketOrder': false, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'withdraw': true, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766927-39ca2ada-5eeb-11e7-972f-1b4199518ca6.jpg', 'api': 'https://api.zaif.jp', 'www': 'https://zaif.jp', 'doc': [ 'https://techbureau-api-document.readthedocs.io/ja/latest/index.html', 'https://corp.zaif.jp/api-docs', 'https://corp.zaif.jp/api-docs/api_links', 'https://www.npmjs.com/package/zaif.jp', 'https://github.com/you21979/node-zaif', ], 'fees': 'https://zaif.jp/fee?lang=en', }, 'fees': { 'trading': { 'percentage': true, 'taker': 0.1 / 100, 'maker': 0, }, }, 'api': { 'public': { 'get': [ 'depth/{pair}', 'currencies/{pair}', 'currencies/all', 'currency_pairs/{pair}', 'currency_pairs/all', 'last_price/{pair}', 'ticker/{pair}', 'trades/{pair}', ], }, 'private': { 'post': [ 'active_orders', 'cancel_order', 'deposit_history', 'get_id_info', 'get_info', 'get_info2', 'get_personal_info', 'trade', 'trade_history', 'withdraw', 'withdraw_history', ], }, 'ecapi': { 'post': [ 'createInvoice', 'getInvoice', 'getInvoiceIdsByOrderNumber', 'cancelInvoice', ], }, 'tlapi': { 'post': [ 'get_positions', 'position_history', 'active_positions', 'create_position', 'change_position', 'cancel_position', ], }, 'fapi': { 'get': [ 'groups/{group_id}', 'last_price/{group_id}/{pair}', 'ticker/{group_id}/{pair}', 'trades/{group_id}/{pair}', 'depth/{group_id}/{pair}', ], }, }, 'options': { // zaif schedule defines several market-specific fees 'fees': { 'BTC/JPY': { 'maker': 0, 'taker': 0 }, 'BCH/JPY': { 'maker': 0, 'taker': 0.3 / 100 }, 'BCH/BTC': { 'maker': 0, 'taker': 0.3 / 100 }, 'PEPECASH/JPY': { 'maker': 0, 'taker': 0.01 / 100 }, 'PEPECASH/BT': { 'maker': 0, 'taker': 0.01 / 100 }, }, }, 'exceptions': { 'exact': { 'unsupported currency_pair': BadRequest, // {"error": "unsupported currency_pair"} }, 'broad': { }, }, }); } async fetchMarkets (params = {}) { const markets = await this.publicGetCurrencyPairsAll (params); const result = []; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString (market, 'currency_pair'); const name = this.safeString (market, 'name'); const [ baseId, quoteId ] = name.split ('/'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': -Math.log10 (market['item_unit_step']), 'price': market['aux_unit_point'], }; const fees = this.safeValue (this.options['fees'], symbol, this.fees['trading']); const taker = fees['taker']; const maker = fees['maker']; result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'active': true, // can trade or not 'precision': precision, 'taker': taker, 'maker': maker, 'limits': { 'amount': { 'min': this.safeFloat (market, 'item_unit_min'), 'max': undefined, }, 'price': { 'min': this.safeFloat (market, 'aux_unit_min'), 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privatePostGetInfo (params); const balances = this.safeValue (response, 'return', {}); const result = { 'info': response }; const funds = this.safeValue (balances, 'funds', {}); const currencyIds = Object.keys (funds); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode (currencyId); const balance = this.safeValue (funds, currencyId); const account = { 'free': balance, 'used': 0.0, 'total': balance, }; if ('deposit' in balances) { if (currencyId in balances['deposit']) { account['total'] = this.safeFloat (balances['deposit'], currencyId); account['used'] = account['total'] - account['free']; } } result[code] = account; } return this.parseBalance (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; const response = await this.publicGetDepthPair (this.extend (request, params)); return this.parseOrderBook (response); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'pair': this.marketId (symbol), }; const ticker = await this.publicGetTickerPair (this.extend (request, params)); const timestamp = this.milliseconds (); const vwap = this.safeFloat (ticker, 'vwap'); const baseVolume = this.safeFloat (ticker, 'volume'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } parseTrade (trade, market = undefined) { let side = this.safeString (trade, 'trade_type'); side = (side === 'bid') ? 'buy' : 'sell'; const timestamp = this.safeTimestamp (trade, 'date'); const id = this.safeString2 (trade, 'id', 'tid'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = amount * price; } } if (market === undefined) { const marketId = this.safeString (trade, 'currency_pair'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'pair': market['id'], }; let response = await this.publicGetTradesPair (this.extend (request, params)); const numTrades = response.length; if (numTrades === 1) { const firstTrade = response[0]; if (!Object.keys (firstTrade).length) { response = []; } } return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); if (type !== 'limit') { throw new ExchangeError (this.id + ' allows limit orders only'); } const request = { 'currency_pair': this.marketId (symbol), 'action': (side === 'buy') ? 'bid' : 'ask', 'amount': amount, 'price': price, }; const response = await this.privatePostTrade (this.extend (request, params)); return { 'info': response, 'id': response['return']['order_id'].toString (), }; } async cancelOrder (id, symbol = undefined, params = {}) { const request = { 'order_id': id, }; return await this.privatePostCancelOrder (this.extend (request, params)); } parseOrder (order, market = undefined) { // // { // "currency_pair": "btc_jpy", // "action": "ask", // "amount": 0.03, // "price": 56000, // "timestamp": 1402021125, // "comment" : "demo" // } // let side = this.safeString (order, 'action'); side = (side === 'bid') ? 'buy' : 'sell'; const timestamp = this.safeTimestamp (order, 'timestamp'); if (!market) { const marketId = this.safeString (order, 'currency_pair'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } } const price = this.safeFloat (order, 'price'); const amount = this.safeFloat (order, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } const id = this.safeString (order, 'id'); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': 'open', 'symbol': symbol, 'type': 'limit', 'side': side, 'price': price, 'cost': cost, 'amount': amount, 'filled': undefined, 'remaining': undefined, 'trades': undefined, 'fee': undefined, 'info': undefined, 'average': undefined, }; } parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { const result = []; const ids = Object.keys (orders); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } for (let i = 0; i < ids.length; i++) { const id = ids[i]; const order = this.extend ({ 'id': id }, orders[id]); result.push (this.extend (this.parseOrder (order, market), params)); } return this.filterBySymbolSinceLimit (result, symbol, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = { // 'is_token': false, // 'is_token_both': false, }; if (symbol !== undefined) { market = this.market (symbol); request['currency_pair'] = market['id']; } const response = await this.privatePostActiveOrders (this.extend (request, params)); return this.parseOrders (response['return'], market, since, limit); } async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); let market = undefined; const request = { // 'from': 0, // 'count': 1000, // 'from_id': 0, // 'end_id': 1000, // 'order': 'DESC', // 'since': 1503821051, // 'end': 1503821051, // 'is_token': false, }; if (symbol !== undefined) { market = this.market (symbol); request['currency_pair'] = market['id']; } const response = await this.privatePostTradeHistory (this.extend (request, params)); return this.parseOrders (response['return'], market, since, limit); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); const currency = this.currency (code); if (code === 'JPY') { throw new ExchangeError (this.id + ' withdraw() does not allow ' + code + ' withdrawals'); } const request = { 'currency': currency['id'], 'amount': amount, 'address': address, // 'message': 'Hi!', // XEM and others // 'opt_fee': 0.003, // BTC and MONA only }; if (tag !== undefined) { request['message'] = tag; } const result = await this.privatePostWithdraw (this.extend (request, params)); return { 'info': result, 'id': result['return']['txid'], 'fee': result['return']['fee'], }; } nonce () { const nonce = parseFloat (this.milliseconds () / 1000); return nonce.toFixed (8); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'] + '/'; if (api === 'public') { url += 'api/' + this.version + '/' + this.implodeParams (path, params); } else if (api === 'fapi') { url += 'fapi/' + this.version + '/' + this.implodeParams (path, params); } else { this.checkRequiredCredentials (); if (api === 'ecapi') { url += 'ecapi'; } else if (api === 'tlapi') { url += 'tlapi'; } else { url += 'tapi'; } const nonce = this.nonce (); body = this.urlencode (this.extend ({ 'method': path, 'nonce': nonce, }, params)); headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Key': this.apiKey, 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), }; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; } // // {"error": "unsupported currency_pair"} // const feedback = this.id + ' ' + body; const error = this.safeString (response, 'error'); if (error !== undefined) { this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); throw new ExchangeError (feedback); // unknown message } const success = this.safeValue (response, 'success', true); if (!success) { throw new ExchangeError (feedback); } } }; },{"./base/Exchange":7,"./base/errors":9}],172:[function(require,module,exports){ 'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { BadRequest, ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, OrderNotFound, ExchangeNotAvailable, DDoSProtection, InvalidOrder } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class zb extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'zb', 'name': 'ZB', 'countries': [ 'CN' ], 'rateLimit': 1000, 'version': 'v1', 'has': { 'CORS': false, 'createMarketOrder': false, 'fetchDepositAddress': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchOpenOrders': true, 'fetchOHLCV': true, 'fetchTickers': true, 'withdraw': true, }, 'timeframes': { '1m': '1min', '3m': '3min', '5m': '5min', '15m': '15min', '30m': '30min', '1h': '1hour', '2h': '2hour', '4h': '4hour', '6h': '6hour', '12h': '12hour', '1d': '1day', '3d': '3day', '1w': '1week', }, 'exceptions': { // '1000': 'Successful operation', '1001': ExchangeError, // 'General error message', '1002': ExchangeError, // 'Internal error', '1003': AuthenticationError, // 'Verification does not pass', '1004': AuthenticationError, // 'Funding security password lock', '1005': AuthenticationError, // 'Funds security password is incorrect, please confirm and re-enter.', '1006': AuthenticationError, // 'Real-name certification pending approval or audit does not pass', '1009': ExchangeNotAvailable, // 'This interface is under maintenance', '2001': InsufficientFunds, // 'Insufficient CNY Balance', '2002': InsufficientFunds, // 'Insufficient BTC Balance', '2003': InsufficientFunds, // 'Insufficient LTC Balance', '2005': InsufficientFunds, // 'Insufficient ETH Balance', '2006': InsufficientFunds, // 'Insufficient ETC Balance', '2007': InsufficientFunds, // 'Insufficient BTS Balance', '2009': InsufficientFunds, // 'Account balance is not enough', '3001': OrderNotFound, // 'Pending orders not found', '3002': InvalidOrder, // 'Invalid price', '3003': InvalidOrder, // 'Invalid amount', '3004': AuthenticationError, // 'User does not exist', '3005': BadRequest, // 'Invalid parameter', '3006': AuthenticationError, // 'Invalid IP or inconsistent with the bound IP', '3007': AuthenticationError, // 'The request time has expired', '3008': OrderNotFound, // 'Transaction records not found', '4001': ExchangeNotAvailable, // 'API interface is locked or not enabled', '4002': DDoSProtection, // 'Request too often', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/32859187-cd5214f0-ca5e-11e7-967d-96568e2e2bd1.jpg', 'api': { 'public': 'http://api.zb.cn/data', // no https for public API 'private': 'https://trade.zb.cn/api', }, 'www': 'https://www.zb.com', 'doc': 'https://www.zb.com/i/developer', 'fees': 'https://www.zb.com/i/rate', }, 'api': { 'public': { 'get': [ 'markets', 'ticker', 'allTicker', 'depth', 'trades', 'kline', ], }, 'private': { 'get': [ // spot API 'order', 'cancelOrder', 'getOrder', 'getOrders', 'getOrdersNew', 'getOrdersIgnoreTradeType', 'getUnfinishedOrdersIgnoreTradeType', 'getAccountInfo', 'getUserAddress', 'getWithdrawAddress', 'getWithdrawRecord', 'getChargeRecord', 'getCnyWithdrawRecord', 'getCnyChargeRecord', 'withdraw', // leverage API 'getLeverAssetsInfo', 'getLeverBills', 'transferInLever', 'transferOutLever', 'loan', 'cancelLoan', 'getLoans', 'getLoanRecords', 'borrow', 'repay', 'getRepayments', ], }, }, 'fees': { 'funding': { 'withdraw': { 'BTC': 0.0001, 'BCH': 0.0006, 'LTC': 0.005, 'ETH': 0.01, 'ETC': 0.01, 'BTS': 3, 'EOS': 1, 'QTUM': 0.01, 'HSR': 0.001, 'XRP': 0.1, 'USDT': '0.1%', 'QCASH': 5, 'DASH': 0.002, 'BCD': 0, 'UBTC': 0, 'SBTC': 0, 'INK': 20, 'TV': 0.1, 'BTH': 0, 'BCX': 0, 'LBTC': 0, 'CHAT': 20, 'bitCNY': 20, 'HLC': 20, 'BTP': 0, 'BCW': 0, }, }, 'trading': { 'maker': 0.2 / 100, 'taker': 0.2 / 100, }, }, 'commonCurrencies': { 'ENT': 'ENTCash', }, }); } async fetchMarkets (params = {}) { const markets = await this.publicGetMarkets (params); const keys = Object.keys (markets); const result = []; for (let i = 0; i < keys.length; i++) { const id = keys[i]; const market = markets[id]; const [ baseId, quoteId ] = id.split ('_'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (market, 'amountScale'), 'price': this.safeInteger (market, 'priceScale'), }; result.push ({ 'id': id, 'symbol': symbol, 'baseId': baseId, 'quoteId': quoteId, 'base': base, 'quote': quote, 'active': true, 'precision': precision, 'limits': { 'amount': { 'min': Math.pow (10, -precision['amount']), 'max': undefined, }, 'price': { 'min': Math.pow (10, -precision['price']), 'max': undefined, }, 'cost': { 'min': 0, 'max': undefined, }, }, 'info': market, }); } return result; } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.privateGetGetAccountInfo (params); // todo: use this somehow // let permissions = response['result']['base']; const balances = this.safeValue (response['result'], 'coins'); const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; // { enName: "BTC", // freez: "0.00000000", // unitDecimal: 8, // always 8 // cnName: "BTC", // isCanRecharge: true, // TODO: should use this // unitTag: "฿", // isCanWithdraw: true, // TODO: should use this // available: "0.00000000", // key: "btc" } const account = this.account (); const currencyId = this.safeString (balance, 'key'); const code = this.safeCurrencyCode (currencyId); account['free'] = this.safeFloat (balance, 'available'); account['used'] = this.safeFloat (balance, 'freez'); result[code] = account; } return this.parseBalance (result); } getMarketFieldName () { return 'market'; } async fetchDepositAddress (code, params = {}) { await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const response = await this.privateGetGetUserAddress (this.extend (request, params)); let address = response['message']['datas']['key']; let tag = undefined; if (address.indexOf ('_') >= 0) { const parts = address.split ('_'); address = parts[0]; // WARNING: MAY BE tag_address INSTEAD OF address_tag FOR SOME CURRENCIES!! tag = parts[1]; } return { 'currency': code, 'address': address, 'tag': tag, 'info': response, }; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const marketFieldName = this.getMarketFieldName (); const request = {}; request[marketFieldName] = market['id']; if (limit !== undefined) { request['size'] = limit; } const response = await this.publicGetDepth (this.extend (request, params)); return this.parseOrderBook (response); } async fetchTickers (symbols = undefined, params = {}) { await this.loadMarkets (); const response = await this.publicGetAllTicker (params); const result = {}; const anotherMarketsById = {}; const marketIds = Object.keys (this.marketsById); for (let i = 0; i < marketIds.length; i++) { const tickerId = marketIds[i].replace ('_', ''); anotherMarketsById[tickerId] = this.marketsById[marketIds[i]]; } const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const market = anotherMarketsById[ids[i]]; result[market['symbol']] = this.parseTicker (response[ids[i]], market); } return result; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const marketFieldName = this.getMarketFieldName (); const request = {}; request[marketFieldName] = market['id']; const response = await this.publicGetTicker (this.extend (request, params)); const ticker = response['ticker']; return this.parseTicker (ticker, market); } parseTicker (ticker, market = undefined) { const timestamp = this.milliseconds (); let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high'), 'low': this.safeFloat (ticker, 'low'), 'bid': this.safeFloat (ticker, 'buy'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'sell'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'vol'), 'quoteVolume': undefined, 'info': ticker, }; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); if (limit === undefined) { limit = 1000; } const request = { 'market': market['id'], 'type': this.timeframes[timeframe], 'limit': limit, }; if (since !== undefined) { request['since'] = since; } const response = await this.publicGetKline (this.extend (request, params)); const data = this.safeValue (response, 'data', []); return this.parseOHLCVs (data, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { const timestamp = this.safeTimestamp (trade, 'date'); let side = this.safeString (trade, 'trade_type'); side = (side === 'bid') ? 'buy' : 'sell'; const id = this.safeString (trade, 'tid'); const price = this.safeFloat (trade, 'price'); const amount = this.safeFloat (trade, 'amount'); let cost = undefined; if (price !== undefined) { if (amount !== undefined) { cost = price * amount; } } let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } return { 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'order': undefined, 'takerOrMaker': undefined, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const marketFieldName = this.getMarketFieldName (); const request = {}; request[marketFieldName] = market['id']; const response = await this.publicGetTrades (this.extend (request, params)); return this.parseTrades (response, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { if (type !== 'limit') { throw new InvalidOrder (this.id + ' allows limit orders only'); } await this.loadMarkets (); const request = { 'price': this.priceToPrecision (symbol, price), 'amount': this.amountToPrecision (symbol, amount), 'tradeType': (side === 'buy') ? '1' : '0', 'currency': this.marketId (symbol), }; const response = await this.privateGetOrder (this.extend (request, params)); return { 'info': response, 'id': response['id'], }; } async cancelOrder (id, symbol = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': id.toString (), 'currency': this.marketId (symbol), }; return await this.privateGetCancelOrder (this.extend (request, params)); } async fetchOrder (id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument'); } await this.loadMarkets (); const request = { 'id': id.toString (), 'currency': this.marketId (symbol), }; const response = await this.privateGetGetOrder (this.extend (request, params)); // // { // 'total_amount': 0.01, // 'id': '20180910244276459', // 'price': 180.0, // 'trade_date': 1536576744960, // 'status': 2, // 'trade_money': '1.96742', // 'trade_amount': 0.01, // 'type': 0, // 'currency': 'eth_usdt' // } // return this.parseOrder (response, undefined); } async fetchOrders (symbol = undefined, since = undefined, limit = 50, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + 'fetchOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], 'pageIndex': 1, // default pageIndex is 1 'pageSize': limit, // default pageSize is 50 }; let method = 'privateGetGetOrdersIgnoreTradeType'; // tradeType 交易类型1/0[buy/sell] if ('tradeType' in params) { method = 'privateGetGetOrdersNew'; } let response = undefined; try { response = await this[method] (this.extend (request, params)); } catch (e) { if (e instanceof OrderNotFound) { return []; } throw e; } return this.parseOrders (response, market, since, limit); } async fetchOpenOrders (symbol = undefined, since = undefined, limit = 10, params = {}) { if (symbol === undefined) { throw new ArgumentsRequired (this.id + 'fetchOpenOrders requires a symbol argument'); } await this.loadMarkets (); const market = this.market (symbol); const request = { 'currency': market['id'], 'pageIndex': 1, // default pageIndex is 1 'pageSize': limit, // default pageSize is 10 }; let method = 'privateGetGetUnfinishedOrdersIgnoreTradeType'; // tradeType 交易类型1/0[buy/sell] if ('tradeType' in params) { method = 'privateGetGetOrdersNew'; } let response = undefined; try { response = await this[method] (this.extend (request, params)); } catch (e) { if (e instanceof OrderNotFound) { return []; } throw e; } return this.parseOrders (response, market, since, limit); } parseOrder (order, market = undefined) { // // fetchOrder // // { // 'total_amount': 0.01, // 'id': '20180910244276459', // 'price': 180.0, // 'trade_date': 1536576744960, // 'status': 2, // 'trade_money': '1.96742', // 'trade_amount': 0.01, // 'type': 0, // 'currency': 'eth_usdt' // } // let side = this.safeInteger (order, 'type'); side = (side === 1) ? 'buy' : 'sell'; const type = 'limit'; // market order is not availalbe in ZB let timestamp = undefined; const createDateField = this.getCreateDateField (); if (createDateField in order) { timestamp = order[createDateField]; } let symbol = undefined; const marketId = this.safeString (order, 'currency'); if (marketId in this.markets_by_id) { // get symbol from currency market = this.marketsById[marketId]; } if (market !== undefined) { symbol = market['symbol']; } const price = this.safeFloat (order, 'price'); const filled = this.safeFloat (order, 'trade_amount'); const amount = this.safeFloat (order, 'total_amount'); let remaining = undefined; if (amount !== undefined) { if (filled !== undefined) { remaining = amount - filled; } } const cost = this.safeFloat (order, 'trade_money'); let average = undefined; const status = this.parseOrderStatus (this.safeString (order, 'status')); if ((cost !== undefined) && (filled !== undefined) && (filled > 0)) { average = cost / filled; } const id = this.safeString (order, 'id'); return { 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'average': average, 'cost': cost, 'amount': amount, 'filled': filled, 'remaining': remaining, 'status': status, 'fee': undefined, 'trades': undefined, }; } parseOrderStatus (status) { const statuses = { '0': 'open', '1': 'canceled', '2': 'closed', '3': 'open', // partial }; return this.safeString (statuses, status, status); } getCreateDateField () { return 'trade_date'; } nonce () { return this.milliseconds (); } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.urls['api'][api]; if (api === 'public') { url += '/' + this.version + '/' + path; if (Object.keys (params).length) { url += '?' + this.urlencode (params); } } else { let query = this.keysort (this.extend ({ 'method': path, 'accesskey': this.apiKey, }, params)); const nonce = this.nonce (); query = this.keysort (query); const auth = this.rawencode (query); const secret = this.hash (this.encode (this.secret), 'sha1'); const signature = this.hmac (this.encode (auth), this.encode (secret), 'md5'); const suffix = 'sign=' + signature + '&reqTime=' + nonce.toString (); url += '/' + path + '?' + auth + '&' + suffix; } return { 'url': url, 'method': method, 'body': body, 'headers': headers }; } handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined) { return; // fallback to default error handler } if (body[0] === '{') { const feedback = this.id + ' ' + body; if ('code' in response) { const code = this.safeString (response, 'code'); this.throwExactlyMatchedException (this.exceptions, code, feedback); if (code !== '1000') { throw new ExchangeError (feedback); } } // special case for {"result":false,"message":"服务端忙碌"} (a "Busy Server" reply) const result = this.safeValue (response, 'result'); if (result !== undefined) { if (!result) { const message = this.safeString (response, 'message'); if (message === '服务端忙碌') { throw new ExchangeNotAvailable (feedback); } else { throw new ExchangeError (feedback); } } } } } }; },{"./base/Exchange":7,"./base/errors":9}],173:[function(require,module,exports){ (function (global){ 'use strict'; // compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js // original notice: /*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh * @license MIT */ function compare(a, b) { if (a === b) { return 0; } var x = a.length; var y = b.length; for (var i = 0, len = Math.min(x, y); i < len; ++i) { if (a[i] !== b[i]) { x = a[i]; y = b[i]; break; } } if (x < y) { return -1; } if (y < x) { return 1; } return 0; } function isBuffer(b) { if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { return global.Buffer.isBuffer(b); } return !!(b != null && b._isBuffer); } // based on node assert, original notice: // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 // // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! // // Originally from narwhal.js (http://narwhaljs.org) // Copyright (c) 2009 Thomas Robinson <280north.com> // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the 'Software'), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var util = require('util/'); var hasOwn = Object.prototype.hasOwnProperty; var pSlice = Array.prototype.slice; var functionsHaveNames = (function () { return function foo() {}.name === 'foo'; }()); function pToString (obj) { return Object.prototype.toString.call(obj); } function isView(arrbuf) { if (isBuffer(arrbuf)) { return false; } if (typeof global.ArrayBuffer !== 'function') { return false; } if (typeof ArrayBuffer.isView === 'function') { return ArrayBuffer.isView(arrbuf); } if (!arrbuf) { return false; } if (arrbuf instanceof DataView) { return true; } if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { return true; } return false; } // 1. The assert module provides functions that throw // AssertionError's when particular conditions are not met. The // assert module must conform to the following interface. var assert = module.exports = ok; // 2. The AssertionError is defined in assert. // new assert.AssertionError({ message: message, // actual: actual, // expected: expected }) var regex = /\s*function\s+([^\(\s]*)\s*/; // based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js function getName(func) { if (!util.isFunction(func)) { return; } if (functionsHaveNames) { return func.name; } var str = func.toString(); var match = str.match(regex); return match && match[1]; } assert.AssertionError = function AssertionError(options) { this.name = 'AssertionError'; this.actual = options.actual; this.expected = options.expected; this.operator = options.operator; if (options.message) { this.message = options.message; this.generatedMessage = false; } else { this.message = getMessage(this); this.generatedMessage = true; } var stackStartFunction = options.stackStartFunction || fail; if (Error.captureStackTrace) { Error.captureStackTrace(this, stackStartFunction); } else { // non v8 browsers so we can have a stacktrace var err = new Error(); if (err.stack) { var out = err.stack; // try to strip useless frames var fn_name = getName(stackStartFunction); var idx = out.indexOf('\n' + fn_name); if (idx >= 0) { // once we have located the function frame // we need to strip out everything before it (and its line) var next_line = out.indexOf('\n', idx + 1); out = out.substring(next_line + 1); } this.stack = out; } } }; // assert.AssertionError instanceof Error util.inherits(assert.AssertionError, Error); function truncate(s, n) { if (typeof s === 'string') { return s.length < n ? s : s.slice(0, n); } else { return s; } } function inspect(something) { if (functionsHaveNames || !util.isFunction(something)) { return util.inspect(something); } var rawname = getName(something); var name = rawname ? ': ' + rawname : ''; return '[Function' + name + ']'; } function getMessage(self) { return truncate(inspect(self.actual), 128) + ' ' + self.operator + ' ' + truncate(inspect(self.expected), 128); } // At present only the three keys mentioned above are used and // understood by the spec. Implementations or sub modules can pass // other keys to the AssertionError's constructor - they will be // ignored. // 3. All of the following functions must throw an AssertionError // when a corresponding condition is not met, with a message that // may be undefined if not provided. All assertion methods provide // both the actual and expected values to the assertion error for // display purposes. function fail(actual, expected, message, operator, stackStartFunction) { throw new assert.AssertionError({ message: message, actual: actual, expected: expected, operator: operator, stackStartFunction: stackStartFunction }); } // EXTENSION! allows for well behaved errors defined elsewhere. assert.fail = fail; // 4. Pure assertion tests whether a value is truthy, as determined // by !!guard. // assert.ok(guard, message_opt); // This statement is equivalent to assert.equal(true, !!guard, // message_opt);. To test strictly for the value true, use // assert.strictEqual(true, guard, message_opt);. function ok(value, message) { if (!value) fail(value, true, message, '==', assert.ok); } assert.ok = ok; // 5. The equality assertion tests shallow, coercive equality with // ==. // assert.equal(actual, expected, message_opt); assert.equal = function equal(actual, expected, message) { if (actual != expected) fail(actual, expected, message, '==', assert.equal); }; // 6. The non-equality assertion tests for whether two objects are not equal // with != assert.notEqual(actual, expected, message_opt); assert.notEqual = function notEqual(actual, expected, message) { if (actual == expected) { fail(actual, expected, message, '!=', assert.notEqual); } }; // 7. The equivalence assertion tests a deep equality relation. // assert.deepEqual(actual, expected, message_opt); assert.deepEqual = function deepEqual(actual, expected, message) { if (!_deepEqual(actual, expected, false)) { fail(actual, expected, message, 'deepEqual', assert.deepEqual); } }; assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { if (!_deepEqual(actual, expected, true)) { fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); } }; function _deepEqual(actual, expected, strict, memos) { // 7.1. All identical values are equivalent, as determined by ===. if (actual === expected) { return true; } else if (isBuffer(actual) && isBuffer(expected)) { return compare(actual, expected) === 0; // 7.2. If the expected value is a Date object, the actual value is // equivalent if it is also a Date object that refers to the same time. } else if (util.isDate(actual) && util.isDate(expected)) { return actual.getTime() === expected.getTime(); // 7.3 If the expected value is a RegExp object, the actual value is // equivalent if it is also a RegExp object with the same source and // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). } else if (util.isRegExp(actual) && util.isRegExp(expected)) { return actual.source === expected.source && actual.global === expected.global && actual.multiline === expected.multiline && actual.lastIndex === expected.lastIndex && actual.ignoreCase === expected.ignoreCase; // 7.4. Other pairs that do not both pass typeof value == 'object', // equivalence is determined by ==. } else if ((actual === null || typeof actual !== 'object') && (expected === null || typeof expected !== 'object')) { return strict ? actual === expected : actual == expected; // If both values are instances of typed arrays, wrap their underlying // ArrayBuffers in a Buffer each to increase performance // This optimization requires the arrays to have the same type as checked by // Object.prototype.toString (aka pToString). Never perform binary // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their // bit patterns are not identical. } else if (isView(actual) && isView(expected) && pToString(actual) === pToString(expected) && !(actual instanceof Float32Array || actual instanceof Float64Array)) { return compare(new Uint8Array(actual.buffer), new Uint8Array(expected.buffer)) === 0; // 7.5 For all other Object pairs, including Array objects, equivalence is // determined by having the same number of owned properties (as verified // with Object.prototype.hasOwnProperty.call), the same set of keys // (although not necessarily the same order), equivalent values for every // corresponding key, and an identical 'prototype' property. Note: this // accounts for both named and indexed properties on Arrays. } else if (isBuffer(actual) !== isBuffer(expected)) { return false; } else { memos = memos || {actual: [], expected: []}; var actualIndex = memos.actual.indexOf(actual); if (actualIndex !== -1) { if (actualIndex === memos.expected.indexOf(expected)) { return true; } } memos.actual.push(actual); memos.expected.push(expected); return objEquiv(actual, expected, strict, memos); } } function isArguments(object) { return Object.prototype.toString.call(object) == '[object Arguments]'; } function objEquiv(a, b, strict, actualVisitedObjects) { if (a === null || a === undefined || b === null || b === undefined) return false; // if one is a primitive, the other must be same if (util.isPrimitive(a) || util.isPrimitive(b)) return a === b; if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false; var aIsArgs = isArguments(a); var bIsArgs = isArguments(b); if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) return false; if (aIsArgs) { a = pSlice.call(a); b = pSlice.call(b); return _deepEqual(a, b, strict); } var ka = objectKeys(a); var kb = objectKeys(b); var key, i; // having the same number of owned properties (keys incorporates // hasOwnProperty) if (ka.length !== kb.length) return false; //the same set of keys (although not necessarily the same order), ka.sort(); kb.sort(); //~~~cheap key test for (i = ka.length - 1; i >= 0; i--) { if (ka[i] !== kb[i]) return false; } //equivalent values for every corresponding key, and //~~~possibly expensive deep test for (i = ka.length - 1; i >= 0; i--) { key = ka[i]; if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) return false; } return true; } // 8. The non-equivalence assertion tests for any deep inequality. // assert.notDeepEqual(actual, expected, message_opt); assert.notDeepEqual = function notDeepEqual(actual, expected, message) { if (_deepEqual(actual, expected, false)) { fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); } }; assert.notDeepStrictEqual = notDeepStrictEqual; function notDeepStrictEqual(actual, expected, message) { if (_deepEqual(actual, expected, true)) { fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); } } // 9. The strict equality assertion tests strict equality, as determined by ===. // assert.strictEqual(actual, expected, message_opt); assert.strictEqual = function strictEqual(actual, expected, message) { if (actual !== expected) { fail(actual, expected, message, '===', assert.strictEqual); } }; // 10. The strict non-equality assertion tests for strict inequality, as // determined by !==. assert.notStrictEqual(actual, expected, message_opt); assert.notStrictEqual = function notStrictEqual(actual, expected, message) { if (actual === expected) { fail(actual, expected, message, '!==', assert.notStrictEqual); } }; function expectedException(actual, expected) { if (!actual || !expected) { return false; } if (Object.prototype.toString.call(expected) == '[object RegExp]') { return expected.test(actual); } try { if (actual instanceof expected) { return true; } } catch (e) { // Ignore. The instanceof check doesn't work for arrow functions. } if (Error.isPrototypeOf(expected)) { return false; } return expected.call({}, actual) === true; } function _tryBlock(block) { var error; try { block(); } catch (e) { error = e; } return error; } function _throws(shouldThrow, block, expected, message) { var actual; if (typeof block !== 'function') { throw new TypeError('"block" argument must be a function'); } if (typeof expected === 'string') { message = expected; expected = null; } actual = _tryBlock(block); message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + (message ? ' ' + message : '.'); if (shouldThrow && !actual) { fail(actual, expected, 'Missing expected exception' + message); } var userProvidedMessage = typeof message === 'string'; var isUnwantedException = !shouldThrow && util.isError(actual); var isUnexpectedException = !shouldThrow && actual && !expected; if ((isUnwantedException && userProvidedMessage && expectedException(actual, expected)) || isUnexpectedException) { fail(actual, expected, 'Got unwanted exception' + message); } if ((shouldThrow && actual && expected && !expectedException(actual, expected)) || (!shouldThrow && actual)) { throw actual; } } // 11. Expected to throw an error: // assert.throws(block, Error_opt, message_opt); assert.throws = function(block, /*optional*/error, /*optional*/message) { _throws(true, block, error, message); }; // EXTENSION! This is annoying to write outside this module. assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { _throws(false, block, error, message); }; assert.ifError = function(err) { if (err) throw err; }; var objectKeys = Object.keys || function (obj) { var keys = []; for (var key in obj) { if (hasOwn.call(obj, key)) keys.push(key); } return keys; }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"util/":231}],174:[function(require,module,exports){ 'use strict' exports.byteLength = byteLength exports.toByteArray = toByteArray exports.fromByteArray = fromByteArray var lookup = [] var revLookup = [] var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' for (var i = 0, len = code.length; i < len; ++i) { lookup[i] = code[i] revLookup[code.charCodeAt(i)] = i } revLookup['-'.charCodeAt(0)] = 62 revLookup['_'.charCodeAt(0)] = 63 function placeHoldersCount (b64) { var len = b64.length if (len % 4 > 0) { throw new Error('Invalid string. Length must be a multiple of 4') } // the number of equal signs (place holders) // if there are two placeholders, than the two characters before it // represent one byte // if there is only one, then the three characters before it represent 2 bytes // this is just a cheap hack to not do indexOf twice return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0 } function byteLength (b64) { // base64 is 4/3 + up to two characters of the original data return (b64.length * 3 / 4) - placeHoldersCount(b64) } function toByteArray (b64) { var i, l, tmp, placeHolders, arr var len = b64.length placeHolders = placeHoldersCount(b64) arr = new Arr((len * 3 / 4) - placeHolders) // if there are placeholders, only get up to the last complete 4 chars l = placeHolders > 0 ? len - 4 : len var L = 0 for (i = 0; i < l; i += 4) { tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] arr[L++] = (tmp >> 16) & 0xFF arr[L++] = (tmp >> 8) & 0xFF arr[L++] = tmp & 0xFF } if (placeHolders === 2) { tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) arr[L++] = tmp & 0xFF } else if (placeHolders === 1) { tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2) arr[L++] = (tmp >> 8) & 0xFF arr[L++] = tmp & 0xFF } return arr } function tripletToBase64 (num) { return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] } function encodeChunk (uint8, start, end) { var tmp var output = [] for (var i = start; i < end; i += 3) { tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) output.push(tripletToBase64(tmp)) } return output.join('') } function fromByteArray (uint8) { var tmp var len = uint8.length var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes var output = '' var parts = [] var maxChunkLength = 16383 // must be multiple of 3 // go through the array every three bytes, we'll deal with trailing stuff later for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) } // pad the end with zeros, but make sure to not forget the extra bytes if (extraBytes === 1) { tmp = uint8[len - 1] output += lookup[tmp >> 2] output += lookup[(tmp << 4) & 0x3F] output += '==' } else if (extraBytes === 2) { tmp = (uint8[len - 2] << 8) + (uint8[len - 1]) output += lookup[tmp >> 10] output += lookup[(tmp >> 4) & 0x3F] output += lookup[(tmp << 2) & 0x3F] output += '=' } parts.push(output) return parts.join('') } },{}],175:[function(require,module,exports){ },{}],176:[function(require,module,exports){ (function (process,Buffer){ 'use strict'; /* eslint camelcase: "off" */ var assert = require('assert'); var Zstream = require('pako/lib/zlib/zstream'); var zlib_deflate = require('pako/lib/zlib/deflate.js'); var zlib_inflate = require('pako/lib/zlib/inflate.js'); var constants = require('pako/lib/zlib/constants'); for (var key in constants) { exports[key] = constants[key]; } // zlib modes exports.NONE = 0; exports.DEFLATE = 1; exports.INFLATE = 2; exports.GZIP = 3; exports.GUNZIP = 4; exports.DEFLATERAW = 5; exports.INFLATERAW = 6; exports.UNZIP = 7; var GZIP_HEADER_ID1 = 0x1f; var GZIP_HEADER_ID2 = 0x8b; /** * Emulate Node's zlib C++ layer for use by the JS layer in index.js */ function Zlib(mode) { if (typeof mode !== 'number' || mode < exports.DEFLATE || mode > exports.UNZIP) { throw new TypeError('Bad argument'); } this.dictionary = null; this.err = 0; this.flush = 0; this.init_done = false; this.level = 0; this.memLevel = 0; this.mode = mode; this.strategy = 0; this.windowBits = 0; this.write_in_progress = false; this.pending_close = false; this.gzip_id_bytes_read = 0; } Zlib.prototype.close = function () { if (this.write_in_progress) { this.pending_close = true; return; } this.pending_close = false; assert(this.init_done, 'close before init'); assert(this.mode <= exports.UNZIP); if (this.mode === exports.DEFLATE || this.mode === exports.GZIP || this.mode === exports.DEFLATERAW) { zlib_deflate.deflateEnd(this.strm); } else if (this.mode === exports.INFLATE || this.mode === exports.GUNZIP || this.mode === exports.INFLATERAW || this.mode === exports.UNZIP) { zlib_inflate.inflateEnd(this.strm); } this.mode = exports.NONE; this.dictionary = null; }; Zlib.prototype.write = function (flush, input, in_off, in_len, out, out_off, out_len) { return this._write(true, flush, input, in_off, in_len, out, out_off, out_len); }; Zlib.prototype.writeSync = function (flush, input, in_off, in_len, out, out_off, out_len) { return this._write(false, flush, input, in_off, in_len, out, out_off, out_len); }; Zlib.prototype._write = function (async, flush, input, in_off, in_len, out, out_off, out_len) { assert.equal(arguments.length, 8); assert(this.init_done, 'write before init'); assert(this.mode !== exports.NONE, 'already finalized'); assert.equal(false, this.write_in_progress, 'write already in progress'); assert.equal(false, this.pending_close, 'close is pending'); this.write_in_progress = true; assert.equal(false, flush === undefined, 'must provide flush value'); this.write_in_progress = true; if (flush !== exports.Z_NO_FLUSH && flush !== exports.Z_PARTIAL_FLUSH && flush !== exports.Z_SYNC_FLUSH && flush !== exports.Z_FULL_FLUSH && flush !== exports.Z_FINISH && flush !== exports.Z_BLOCK) { throw new Error('Invalid flush value'); } if (input == null) { input = Buffer.alloc(0); in_len = 0; in_off = 0; } this.strm.avail_in = in_len; this.strm.input = input; this.strm.next_in = in_off; this.strm.avail_out = out_len; this.strm.output = out; this.strm.next_out = out_off; this.flush = flush; if (!async) { // sync version this._process(); if (this._checkError()) { return this._afterSync(); } return; } // async version var self = this; process.nextTick(function () { self._process(); self._after(); }); return this; }; Zlib.prototype._afterSync = function () { var avail_out = this.strm.avail_out; var avail_in = this.strm.avail_in; this.write_in_progress = false; return [avail_in, avail_out]; }; Zlib.prototype._process = function () { var next_expected_header_byte = null; // If the avail_out is left at 0, then it means that it ran out // of room. If there was avail_out left over, then it means // that all of the input was consumed. switch (this.mode) { case exports.DEFLATE: case exports.GZIP: case exports.DEFLATERAW: this.err = zlib_deflate.deflate(this.strm, this.flush); break; case exports.UNZIP: if (this.strm.avail_in > 0) { next_expected_header_byte = this.strm.next_in; } switch (this.gzip_id_bytes_read) { case 0: if (next_expected_header_byte === null) { break; } if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID1) { this.gzip_id_bytes_read = 1; next_expected_header_byte++; if (this.strm.avail_in === 1) { // The only available byte was already read. break; } } else { this.mode = exports.INFLATE; break; } // fallthrough case 1: if (next_expected_header_byte === null) { break; } if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID2) { this.gzip_id_bytes_read = 2; this.mode = exports.GUNZIP; } else { // There is no actual difference between INFLATE and INFLATERAW // (after initialization). this.mode = exports.INFLATE; } break; default: throw new Error('invalid number of gzip magic number bytes read'); } // fallthrough case exports.INFLATE: case exports.GUNZIP: case exports.INFLATERAW: this.err = zlib_inflate.inflate(this.strm, this.flush // If data was encoded with dictionary );if (this.err === exports.Z_NEED_DICT && this.dictionary) { // Load it this.err = zlib_inflate.inflateSetDictionary(this.strm, this.dictionary); if (this.err === exports.Z_OK) { // And try to decode again this.err = zlib_inflate.inflate(this.strm, this.flush); } else if (this.err === exports.Z_DATA_ERROR) { // Both inflateSetDictionary() and inflate() return Z_DATA_ERROR. // Make it possible for After() to tell a bad dictionary from bad // input. this.err = exports.Z_NEED_DICT; } } while (this.strm.avail_in > 0 && this.mode === exports.GUNZIP && this.err === exports.Z_STREAM_END && this.strm.next_in[0] !== 0x00) { // Bytes remain in input buffer. Perhaps this is another compressed // member in the same archive, or just trailing garbage. // Trailing zero bytes are okay, though, since they are frequently // used for padding. this.reset(); this.err = zlib_inflate.inflate(this.strm, this.flush); } break; default: throw new Error('Unknown mode ' + this.mode); } }; Zlib.prototype._checkError = function () { // Acceptable error states depend on the type of zlib stream. switch (this.err) { case exports.Z_OK: case exports.Z_BUF_ERROR: if (this.strm.avail_out !== 0 && this.flush === exports.Z_FINISH) { this._error('unexpected end of file'); return false; } break; case exports.Z_STREAM_END: // normal statuses, not fatal break; case exports.Z_NEED_DICT: if (this.dictionary == null) { this._error('Missing dictionary'); } else { this._error('Bad dictionary'); } return false; default: // something else. this._error('Zlib error'); return false; } return true; }; Zlib.prototype._after = function () { if (!this._checkError()) { return; } var avail_out = this.strm.avail_out; var avail_in = this.strm.avail_in; this.write_in_progress = false; // call the write() cb this.callback(avail_in, avail_out); if (this.pending_close) { this.close(); } }; Zlib.prototype._error = function (message) { if (this.strm.msg) { message = this.strm.msg; } this.onerror(message, this.err // no hope of rescue. );this.write_in_progress = false; if (this.pending_close) { this.close(); } }; Zlib.prototype.init = function (windowBits, level, memLevel, strategy, dictionary) { assert(arguments.length === 4 || arguments.length === 5, 'init(windowBits, level, memLevel, strategy, [dictionary])'); assert(windowBits >= 8 && windowBits <= 15, 'invalid windowBits'); assert(level >= -1 && level <= 9, 'invalid compression level'); assert(memLevel >= 1 && memLevel <= 9, 'invalid memlevel'); assert(strategy === exports.Z_FILTERED || strategy === exports.Z_HUFFMAN_ONLY || strategy === exports.Z_RLE || strategy === exports.Z_FIXED || strategy === exports.Z_DEFAULT_STRATEGY, 'invalid strategy'); this._init(level, windowBits, memLevel, strategy, dictionary); this._setDictionary(); }; Zlib.prototype.params = function () { throw new Error('deflateParams Not supported'); }; Zlib.prototype.reset = function () { this._reset(); this._setDictionary(); }; Zlib.prototype._init = function (level, windowBits, memLevel, strategy, dictionary) { this.level = level; this.windowBits = windowBits; this.memLevel = memLevel; this.strategy = strategy; this.flush = exports.Z_NO_FLUSH; this.err = exports.Z_OK; if (this.mode === exports.GZIP || this.mode === exports.GUNZIP) { this.windowBits += 16; } if (this.mode === exports.UNZIP) { this.windowBits += 32; } if (this.mode === exports.DEFLATERAW || this.mode === exports.INFLATERAW) { this.windowBits = -1 * this.windowBits; } this.strm = new Zstream(); switch (this.mode) { case exports.DEFLATE: case exports.GZIP: case exports.DEFLATERAW: this.err = zlib_deflate.deflateInit2(this.strm, this.level, exports.Z_DEFLATED, this.windowBits, this.memLevel, this.strategy); break; case exports.INFLATE: case exports.GUNZIP: case exports.INFLATERAW: case exports.UNZIP: this.err = zlib_inflate.inflateInit2(this.strm, this.windowBits); break; default: throw new Error('Unknown mode ' + this.mode); } if (this.err !== exports.Z_OK) { this._error('Init error'); } this.dictionary = dictionary; this.write_in_progress = false; this.init_done = true; }; Zlib.prototype._setDictionary = function () { if (this.dictionary == null) { return; } this.err = exports.Z_OK; switch (this.mode) { case exports.DEFLATE: case exports.DEFLATERAW: this.err = zlib_deflate.deflateSetDictionary(this.strm, this.dictionary); break; default: break; } if (this.err !== exports.Z_OK) { this._error('Failed to set dictionary'); } }; Zlib.prototype._reset = function () { this.err = exports.Z_OK; switch (this.mode) { case exports.DEFLATE: case exports.DEFLATERAW: case exports.GZIP: this.err = zlib_deflate.deflateReset(this.strm); break; case exports.INFLATE: case exports.INFLATERAW: case exports.GUNZIP: this.err = zlib_inflate.inflateReset(this.strm); break; default: break; } if (this.err !== exports.Z_OK) { this._error('Failed to reset stream'); } }; exports.Zlib = Zlib; }).call(this,require('_process'),require("buffer").Buffer) },{"_process":200,"assert":173,"buffer":178,"pako/lib/zlib/constants":190,"pako/lib/zlib/deflate.js":192,"pako/lib/zlib/inflate.js":194,"pako/lib/zlib/zstream":198}],177:[function(require,module,exports){ (function (process){ 'use strict'; var Buffer = require('buffer').Buffer; var Transform = require('stream').Transform; var binding = require('./binding'); var util = require('util'); var assert = require('assert').ok; var kMaxLength = require('buffer').kMaxLength; var kRangeErrorMessage = 'Cannot create final Buffer. It would be larger ' + 'than 0x' + kMaxLength.toString(16) + ' bytes'; // zlib doesn't provide these, so kludge them in following the same // const naming scheme zlib uses. binding.Z_MIN_WINDOWBITS = 8; binding.Z_MAX_WINDOWBITS = 15; binding.Z_DEFAULT_WINDOWBITS = 15; // fewer than 64 bytes per chunk is stupid. // technically it could work with as few as 8, but even 64 bytes // is absurdly low. Usually a MB or more is best. binding.Z_MIN_CHUNK = 64; binding.Z_MAX_CHUNK = Infinity; binding.Z_DEFAULT_CHUNK = 16 * 1024; binding.Z_MIN_MEMLEVEL = 1; binding.Z_MAX_MEMLEVEL = 9; binding.Z_DEFAULT_MEMLEVEL = 8; binding.Z_MIN_LEVEL = -1; binding.Z_MAX_LEVEL = 9; binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION; // expose all the zlib constants var bkeys = Object.keys(binding); for (var bk = 0; bk < bkeys.length; bk++) { var bkey = bkeys[bk]; if (bkey.match(/^Z/)) { Object.defineProperty(exports, bkey, { enumerable: true, value: binding[bkey], writable: false }); } } // translation table for return codes. var codes = { Z_OK: binding.Z_OK, Z_STREAM_END: binding.Z_STREAM_END, Z_NEED_DICT: binding.Z_NEED_DICT, Z_ERRNO: binding.Z_ERRNO, Z_STREAM_ERROR: binding.Z_STREAM_ERROR, Z_DATA_ERROR: binding.Z_DATA_ERROR, Z_MEM_ERROR: binding.Z_MEM_ERROR, Z_BUF_ERROR: binding.Z_BUF_ERROR, Z_VERSION_ERROR: binding.Z_VERSION_ERROR }; var ckeys = Object.keys(codes); for (var ck = 0; ck < ckeys.length; ck++) { var ckey = ckeys[ck]; codes[codes[ckey]] = ckey; } Object.defineProperty(exports, 'codes', { enumerable: true, value: Object.freeze(codes), writable: false }); exports.Deflate = Deflate; exports.Inflate = Inflate; exports.Gzip = Gzip; exports.Gunzip = Gunzip; exports.DeflateRaw = DeflateRaw; exports.InflateRaw = InflateRaw; exports.Unzip = Unzip; exports.createDeflate = function (o) { return new Deflate(o); }; exports.createInflate = function (o) { return new Inflate(o); }; exports.createDeflateRaw = function (o) { return new DeflateRaw(o); }; exports.createInflateRaw = function (o) { return new InflateRaw(o); }; exports.createGzip = function (o) { return new Gzip(o); }; exports.createGunzip = function (o) { return new Gunzip(o); }; exports.createUnzip = function (o) { return new Unzip(o); }; // Convenience methods. // compress/decompress a string or buffer in one step. exports.deflate = function (buffer, opts, callback) { if (typeof opts === 'function') { callback = opts; opts = {}; } return zlibBuffer(new Deflate(opts), buffer, callback); }; exports.deflateSync = function (buffer, opts) { return zlibBufferSync(new Deflate(opts), buffer); }; exports.gzip = function (buffer, opts, callback) { if (typeof opts === 'function') { callback = opts; opts = {}; } return zlibBuffer(new Gzip(opts), buffer, callback); }; exports.gzipSync = function (buffer, opts) { return zlibBufferSync(new Gzip(opts), buffer); }; exports.deflateRaw = function (buffer, opts, callback) { if (typeof opts === 'function') { callback = opts; opts = {}; } return zlibBuffer(new DeflateRaw(opts), buffer, callback); }; exports.deflateRawSync = function (buffer, opts) { return zlibBufferSync(new DeflateRaw(opts), buffer); }; exports.unzip = function (buffer, opts, callback) { if (typeof opts === 'function') { callback = opts; opts = {}; } return zlibBuffer(new Unzip(opts), buffer, callback); }; exports.unzipSync = function (buffer, opts) { return zlibBufferSync(new Unzip(opts), buffer); }; exports.inflate = function (buffer, opts, callback) { if (typeof opts === 'function') { callback = opts; opts = {}; } return zlibBuffer(new Inflate(opts), buffer, callback); }; exports.inflateSync = function (buffer, opts) { return zlibBufferSync(new Inflate(opts), buffer); }; exports.gunzip = function (buffer, opts, callback) { if (typeof opts === 'function') { callback = opts; opts = {}; } return zlibBuffer(new Gunzip(opts), buffer, callback); }; exports.gunzipSync = function (buffer, opts) { return zlibBufferSync(new Gunzip(opts), buffer); }; exports.inflateRaw = function (buffer, opts, callback) { if (typeof opts === 'function') { callback = opts; opts = {}; } return zlibBuffer(new InflateRaw(opts), buffer, callback); }; exports.inflateRawSync = function (buffer, opts) { return zlibBufferSync(new InflateRaw(opts), buffer); }; function zlibBuffer(engine, buffer, callback) { var buffers = []; var nread = 0; engine.on('error', onError); engine.on('end', onEnd); engine.end(buffer); flow(); function flow() { var chunk; while (null !== (chunk = engine.read())) { buffers.push(chunk); nread += chunk.length; } engine.once('readable', flow); } function onError(err) { engine.removeListener('end', onEnd); engine.removeListener('readable', flow); callback(err); } function onEnd() { var buf; var err = null; if (nread >= kMaxLength) { err = new RangeError(kRangeErrorMessage); } else { buf = Buffer.concat(buffers, nread); } buffers = []; engine.close(); callback(err, buf); } } function zlibBufferSync(engine, buffer) { if (typeof buffer === 'string') buffer = Buffer.from(buffer); if (!Buffer.isBuffer(buffer)) throw new TypeError('Not a string or buffer'); var flushFlag = engine._finishFlushFlag; return engine._processChunk(buffer, flushFlag); } // generic zlib // minimal 2-byte header function Deflate(opts) { if (!(this instanceof Deflate)) return new Deflate(opts); Zlib.call(this, opts, binding.DEFLATE); } function Inflate(opts) { if (!(this instanceof Inflate)) return new Inflate(opts); Zlib.call(this, opts, binding.INFLATE); } // gzip - bigger header, same deflate compression function Gzip(opts) { if (!(this instanceof Gzip)) return new Gzip(opts); Zlib.call(this, opts, binding.GZIP); } function Gunzip(opts) { if (!(this instanceof Gunzip)) return new Gunzip(opts); Zlib.call(this, opts, binding.GUNZIP); } // raw - no header function DeflateRaw(opts) { if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); Zlib.call(this, opts, binding.DEFLATERAW); } function InflateRaw(opts) { if (!(this instanceof InflateRaw)) return new InflateRaw(opts); Zlib.call(this, opts, binding.INFLATERAW); } // auto-detect header. function Unzip(opts) { if (!(this instanceof Unzip)) return new Unzip(opts); Zlib.call(this, opts, binding.UNZIP); } function isValidFlushFlag(flag) { return flag === binding.Z_NO_FLUSH || flag === binding.Z_PARTIAL_FLUSH || flag === binding.Z_SYNC_FLUSH || flag === binding.Z_FULL_FLUSH || flag === binding.Z_FINISH || flag === binding.Z_BLOCK; } // the Zlib class they all inherit from // This thing manages the queue of requests, and returns // true or false if there is anything in the queue when // you call the .write() method. function Zlib(opts, mode) { var _this = this; this._opts = opts = opts || {}; this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK; Transform.call(this, opts); if (opts.flush && !isValidFlushFlag(opts.flush)) { throw new Error('Invalid flush flag: ' + opts.flush); } if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) { throw new Error('Invalid flush flag: ' + opts.finishFlush); } this._flushFlag = opts.flush || binding.Z_NO_FLUSH; this._finishFlushFlag = typeof opts.finishFlush !== 'undefined' ? opts.finishFlush : binding.Z_FINISH; if (opts.chunkSize) { if (opts.chunkSize < exports.Z_MIN_CHUNK || opts.chunkSize > exports.Z_MAX_CHUNK) { throw new Error('Invalid chunk size: ' + opts.chunkSize); } } if (opts.windowBits) { if (opts.windowBits < exports.Z_MIN_WINDOWBITS || opts.windowBits > exports.Z_MAX_WINDOWBITS) { throw new Error('Invalid windowBits: ' + opts.windowBits); } } if (opts.level) { if (opts.level < exports.Z_MIN_LEVEL || opts.level > exports.Z_MAX_LEVEL) { throw new Error('Invalid compression level: ' + opts.level); } } if (opts.memLevel) { if (opts.memLevel < exports.Z_MIN_MEMLEVEL || opts.memLevel > exports.Z_MAX_MEMLEVEL) { throw new Error('Invalid memLevel: ' + opts.memLevel); } } if (opts.strategy) { if (opts.strategy != exports.Z_FILTERED && opts.strategy != exports.Z_HUFFMAN_ONLY && opts.strategy != exports.Z_RLE && opts.strategy != exports.Z_FIXED && opts.strategy != exports.Z_DEFAULT_STRATEGY) { throw new Error('Invalid strategy: ' + opts.strategy); } } if (opts.dictionary) { if (!Buffer.isBuffer(opts.dictionary)) { throw new Error('Invalid dictionary: it should be a Buffer instance'); } } this._handle = new binding.Zlib(mode); var self = this; this._hadError = false; this._handle.onerror = function (message, errno) { // there is no way to cleanly recover. // continuing only obscures problems. _close(self); self._hadError = true; var error = new Error(message); error.errno = errno; error.code = exports.codes[errno]; self.emit('error', error); }; var level = exports.Z_DEFAULT_COMPRESSION; if (typeof opts.level === 'number') level = opts.level; var strategy = exports.Z_DEFAULT_STRATEGY; if (typeof opts.strategy === 'number') strategy = opts.strategy; this._handle.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS, level, opts.memLevel || exports.Z_DEFAULT_MEMLEVEL, strategy, opts.dictionary); this._buffer = Buffer.allocUnsafe(this._chunkSize); this._offset = 0; this._level = level; this._strategy = strategy; this.once('end', this.close); Object.defineProperty(this, '_closed', { get: function () { return !_this._handle; }, configurable: true, enumerable: true }); } util.inherits(Zlib, Transform); Zlib.prototype.params = function (level, strategy, callback) { if (level < exports.Z_MIN_LEVEL || level > exports.Z_MAX_LEVEL) { throw new RangeError('Invalid compression level: ' + level); } if (strategy != exports.Z_FILTERED && strategy != exports.Z_HUFFMAN_ONLY && strategy != exports.Z_RLE && strategy != exports.Z_FIXED && strategy != exports.Z_DEFAULT_STRATEGY) { throw new TypeError('Invalid strategy: ' + strategy); } if (this._level !== level || this._strategy !== strategy) { var self = this; this.flush(binding.Z_SYNC_FLUSH, function () { assert(self._handle, 'zlib binding closed'); self._handle.params(level, strategy); if (!self._hadError) { self._level = level; self._strategy = strategy; if (callback) callback(); } }); } else { process.nextTick(callback); } }; Zlib.prototype.reset = function () { assert(this._handle, 'zlib binding closed'); return this._handle.reset(); }; // This is the _flush function called by the transform class, // internally, when the last chunk has been written. Zlib.prototype._flush = function (callback) { this._transform(Buffer.alloc(0), '', callback); }; Zlib.prototype.flush = function (kind, callback) { var _this2 = this; var ws = this._writableState; if (typeof kind === 'function' || kind === undefined && !callback) { callback = kind; kind = binding.Z_FULL_FLUSH; } if (ws.ended) { if (callback) process.nextTick(callback); } else if (ws.ending) { if (callback) this.once('end', callback); } else if (ws.needDrain) { if (callback) { this.once('drain', function () { return _this2.flush(kind, callback); }); } } else { this._flushFlag = kind; this.write(Buffer.alloc(0), '', callback); } }; Zlib.prototype.close = function (callback) { _close(this, callback); process.nextTick(emitCloseNT, this); }; function _close(engine, callback) { if (callback) process.nextTick(callback); // Caller may invoke .close after a zlib error (which will null _handle). if (!engine._handle) return; engine._handle.close(); engine._handle = null; } function emitCloseNT(self) { self.emit('close'); } Zlib.prototype._transform = function (chunk, encoding, cb) { var flushFlag; var ws = this._writableState; var ending = ws.ending || ws.ended; var last = ending && (!chunk || ws.length === chunk.length); if (chunk !== null && !Buffer.isBuffer(chunk)) return cb(new Error('invalid input')); if (!this._handle) return cb(new Error('zlib binding closed')); // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag // (or whatever flag was provided using opts.finishFlush). // If it's explicitly flushing at some other time, then we use // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression // goodness. if (last) flushFlag = this._finishFlushFlag;else { flushFlag = this._flushFlag; // once we've flushed the last of the queue, stop flushing and // go back to the normal behavior. if (chunk.length >= ws.length) { this._flushFlag = this._opts.flush || binding.Z_NO_FLUSH; } } this._processChunk(chunk, flushFlag, cb); }; Zlib.prototype._processChunk = function (chunk, flushFlag, cb) { var availInBefore = chunk && chunk.length; var availOutBefore = this._chunkSize - this._offset; var inOff = 0; var self = this; var async = typeof cb === 'function'; if (!async) { var buffers = []; var nread = 0; var error; this.on('error', function (er) { error = er; }); assert(this._handle, 'zlib binding closed'); do { var res = this._handle.writeSync(flushFlag, chunk, // in inOff, // in_off availInBefore, // in_len this._buffer, // out this._offset, //out_off availOutBefore); // out_len } while (!this._hadError && callback(res[0], res[1])); if (this._hadError) { throw error; } if (nread >= kMaxLength) { _close(this); throw new RangeError(kRangeErrorMessage); } var buf = Buffer.concat(buffers, nread); _close(this); return buf; } assert(this._handle, 'zlib binding closed'); var req = this._handle.write(flushFlag, chunk, // in inOff, // in_off availInBefore, // in_len this._buffer, // out this._offset, //out_off availOutBefore); // out_len req.buffer = chunk; req.callback = callback; function callback(availInAfter, availOutAfter) { // When the callback is used in an async write, the callback's // context is the `req` object that was created. The req object // is === this._handle, and that's why it's important to null // out the values after they are done being used. `this._handle` // can stay in memory longer than the callback and buffer are needed. if (this) { this.buffer = null; this.callback = null; } if (self._hadError) return; var have = availOutBefore - availOutAfter; assert(have >= 0, 'have should not go down'); if (have > 0) { var out = self._buffer.slice(self._offset, self._offset + have); self._offset += have; // serve some output to the consumer. if (async) { self.push(out); } else { buffers.push(out); nread += out.length; } } // exhausted the output buffer, or used all the input create a new one. if (availOutAfter === 0 || self._offset >= self._chunkSize) { availOutBefore = self._chunkSize; self._offset = 0; self._buffer = Buffer.allocUnsafe(self._chunkSize); } if (availOutAfter === 0) { // Not actually done. Need to reprocess. // Also, update the availInBefore to the availInAfter value, // so that if we have to hit it a third (fourth, etc.) time, // it'll have the correct byte counts. inOff += availInBefore - availInAfter; availInBefore = availInAfter; if (!async) return true; var newReq = self._handle.write(flushFlag, chunk, inOff, availInBefore, self._buffer, self._offset, self._chunkSize); newReq.callback = callback; // this same function newReq.buffer = chunk; return; } if (!async) return false; // finished with the chunk. cb(); } }; util.inherits(Deflate, Zlib); util.inherits(Inflate, Zlib); util.inherits(Gzip, Zlib); util.inherits(Gunzip, Zlib); util.inherits(DeflateRaw, Zlib); util.inherits(InflateRaw, Zlib); util.inherits(Unzip, Zlib); }).call(this,require('_process')) },{"./binding":176,"_process":200,"assert":173,"buffer":178,"stream":219,"util":231}],178:[function(require,module,exports){ /*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh * @license MIT */ /* eslint-disable no-proto */ 'use strict' var base64 = require('base64-js') var ieee754 = require('ieee754') exports.Buffer = Buffer exports.SlowBuffer = SlowBuffer exports.INSPECT_MAX_BYTES = 50 var K_MAX_LENGTH = 0x7fffffff exports.kMaxLength = K_MAX_LENGTH /** * If `Buffer.TYPED_ARRAY_SUPPORT`: * === true Use Uint8Array implementation (fastest) * === false Print warning and recommend using `buffer` v4.x which has an Object * implementation (most compatible, even IE6) * * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, * Opera 11.6+, iOS 4.2+. * * We report that the browser does not support typed arrays if the are not subclassable * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support * for __proto__ and has a buggy typed array implementation. */ Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && typeof console.error === 'function') { console.error( 'This browser lacks typed array (Uint8Array) support which is required by ' + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' ) } function typedArraySupport () { // Can typed array instances can be augmented? try { var arr = new Uint8Array(1) arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }} return arr.foo() === 42 } catch (e) { return false } } function createBuffer (length) { if (length > K_MAX_LENGTH) { throw new RangeError('Invalid typed array length') } // Return an augmented `Uint8Array` instance var buf = new Uint8Array(length) buf.__proto__ = Buffer.prototype return buf } /** * The Buffer constructor returns instances of `Uint8Array` that have their * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of * `Uint8Array`, so the returned instances will have all the node `Buffer` methods * and the `Uint8Array` methods. Square bracket notation works as expected -- it * returns a single octet. * * The `Uint8Array` prototype remains unmodified. */ function Buffer (arg, encodingOrOffset, length) { // Common case. if (typeof arg === 'number') { if (typeof encodingOrOffset === 'string') { throw new Error( 'If encoding is specified then the first argument must be a string' ) } return allocUnsafe(arg) } return from(arg, encodingOrOffset, length) } // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 if (typeof Symbol !== 'undefined' && Symbol.species && Buffer[Symbol.species] === Buffer) { Object.defineProperty(Buffer, Symbol.species, { value: null, configurable: true, enumerable: false, writable: false }) } Buffer.poolSize = 8192 // not used by this implementation function from (value, encodingOrOffset, length) { if (typeof value === 'number') { throw new TypeError('"value" argument must not be a number') } if (isArrayBuffer(value)) { return fromArrayBuffer(value, encodingOrOffset, length) } if (typeof value === 'string') { return fromString(value, encodingOrOffset) } return fromObject(value) } /** * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError * if value is a number. * Buffer.from(str[, encoding]) * Buffer.from(array) * Buffer.from(buffer) * Buffer.from(arrayBuffer[, byteOffset[, length]]) **/ Buffer.from = function (value, encodingOrOffset, length) { return from(value, encodingOrOffset, length) } // Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: // https://github.com/feross/buffer/pull/148 Buffer.prototype.__proto__ = Uint8Array.prototype Buffer.__proto__ = Uint8Array function assertSize (size) { if (typeof size !== 'number') { throw new TypeError('"size" argument must be a number') } else if (size < 0) { throw new RangeError('"size" argument must not be negative') } } function alloc (size, fill, encoding) { assertSize(size) if (size <= 0) { return createBuffer(size) } if (fill !== undefined) { // Only pay attention to encoding if it's a string. This // prevents accidentally sending in a number that would // be interpretted as a start offset. return typeof encoding === 'string' ? createBuffer(size).fill(fill, encoding) : createBuffer(size).fill(fill) } return createBuffer(size) } /** * Creates a new filled Buffer instance. * alloc(size[, fill[, encoding]]) **/ Buffer.alloc = function (size, fill, encoding) { return alloc(size, fill, encoding) } function allocUnsafe (size) { assertSize(size) return createBuffer(size < 0 ? 0 : checked(size) | 0) } /** * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. * */ Buffer.allocUnsafe = function (size) { return allocUnsafe(size) } /** * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. */ Buffer.allocUnsafeSlow = function (size) { return allocUnsafe(size) } function fromString (string, encoding) { if (typeof encoding !== 'string' || encoding === '') { encoding = 'utf8' } if (!Buffer.isEncoding(encoding)) { throw new TypeError('"encoding" must be a valid string encoding') } var length = byteLength(string, encoding) | 0 var buf = createBuffer(length) var actual = buf.write(string, encoding) if (actual !== length) { // Writing a hex string, for example, that contains invalid characters will // cause everything after the first invalid character to be ignored. (e.g. // 'abxxcd' will be treated as 'ab') buf = buf.slice(0, actual) } return buf } function fromArrayLike (array) { var length = array.length < 0 ? 0 : checked(array.length) | 0 var buf = createBuffer(length) for (var i = 0; i < length; i += 1) { buf[i] = array[i] & 255 } return buf } function fromArrayBuffer (array, byteOffset, length) { if (byteOffset < 0 || array.byteLength < byteOffset) { throw new RangeError('\'offset\' is out of bounds') } if (array.byteLength < byteOffset + (length || 0)) { throw new RangeError('\'length\' is out of bounds') } var buf if (byteOffset === undefined && length === undefined) { buf = new Uint8Array(array) } else if (length === undefined) { buf = new Uint8Array(array, byteOffset) } else { buf = new Uint8Array(array, byteOffset, length) } // Return an augmented `Uint8Array` instance buf.__proto__ = Buffer.prototype return buf } function fromObject (obj) { if (Buffer.isBuffer(obj)) { var len = checked(obj.length) | 0 var buf = createBuffer(len) if (buf.length === 0) { return buf } obj.copy(buf, 0, 0, len) return buf } if (obj) { if (isArrayBufferView(obj) || 'length' in obj) { if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { return createBuffer(0) } return fromArrayLike(obj) } if (obj.type === 'Buffer' && Array.isArray(obj.data)) { return fromArrayLike(obj.data) } } throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.') } function checked (length) { // Note: cannot use `length < K_MAX_LENGTH` here because that fails when // length is NaN (which is otherwise coerced to zero.) if (length >= K_MAX_LENGTH) { throw new RangeError('Attempt to allocate Buffer larger than maximum ' + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') } return length | 0 } function SlowBuffer (length) { if (+length != length) { // eslint-disable-line eqeqeq length = 0 } return Buffer.alloc(+length) } Buffer.isBuffer = function isBuffer (b) { return b != null && b._isBuffer === true } Buffer.compare = function compare (a, b) { if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { throw new TypeError('Arguments must be Buffers') } if (a === b) return 0 var x = a.length var y = b.length for (var i = 0, len = Math.min(x, y); i < len; ++i) { if (a[i] !== b[i]) { x = a[i] y = b[i] break } } if (x < y) return -1 if (y < x) return 1 return 0 } Buffer.isEncoding = function isEncoding (encoding) { switch (String(encoding).toLowerCase()) { case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'latin1': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return true default: return false } } Buffer.concat = function concat (list, length) { if (!Array.isArray(list)) { throw new TypeError('"list" argument must be an Array of Buffers') } if (list.length === 0) { return Buffer.alloc(0) } var i if (length === undefined) { length = 0 for (i = 0; i < list.length; ++i) { length += list[i].length } } var buffer = Buffer.allocUnsafe(length) var pos = 0 for (i = 0; i < list.length; ++i) { var buf = list[i] if (!Buffer.isBuffer(buf)) { throw new TypeError('"list" argument must be an Array of Buffers') } buf.copy(buffer, pos) pos += buf.length } return buffer } function byteLength (string, encoding) { if (Buffer.isBuffer(string)) { return string.length } if (isArrayBufferView(string) || isArrayBuffer(string)) { return string.byteLength } if (typeof string !== 'string') { string = '' + string } var len = string.length if (len === 0) return 0 // Use a for loop to avoid recursion var loweredCase = false for (;;) { switch (encoding) { case 'ascii': case 'latin1': case 'binary': return len case 'utf8': case 'utf-8': case undefined: return utf8ToBytes(string).length case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return len * 2 case 'hex': return len >>> 1 case 'base64': return base64ToBytes(string).length default: if (loweredCase) return utf8ToBytes(string).length // assume utf8 encoding = ('' + encoding).toLowerCase() loweredCase = true } } } Buffer.byteLength = byteLength function slowToString (encoding, start, end) { var loweredCase = false // No need to verify that "this.length <= MAX_UINT32" since it's a read-only // property of a typed array. // This behaves neither like String nor Uint8Array in that we set start/end // to their upper/lower bounds if the value passed is out of range. // undefined is handled specially as per ECMA-262 6th Edition, // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. if (start === undefined || start < 0) { start = 0 } // Return early if start > this.length. Done here to prevent potential uint32 // coercion fail below. if (start > this.length) { return '' } if (end === undefined || end > this.length) { end = this.length } if (end <= 0) { return '' } // Force coersion to uint32. This will also coerce falsey/NaN values to 0. end >>>= 0 start >>>= 0 if (end <= start) { return '' } if (!encoding) encoding = 'utf8' while (true) { switch (encoding) { case 'hex': return hexSlice(this, start, end) case 'utf8': case 'utf-8': return utf8Slice(this, start, end) case 'ascii': return asciiSlice(this, start, end) case 'latin1': case 'binary': return latin1Slice(this, start, end) case 'base64': return base64Slice(this, start, end) case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return utf16leSlice(this, start, end) default: if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) encoding = (encoding + '').toLowerCase() loweredCase = true } } } // This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) // to detect a Buffer instance. It's not possible to use `instanceof Buffer` // reliably in a browserify context because there could be multiple different // copies of the 'buffer' package in use. This method works even for Buffer // instances that were created from another copy of the `buffer` package. // See: https://github.com/feross/buffer/issues/154 Buffer.prototype._isBuffer = true function swap (b, n, m) { var i = b[n] b[n] = b[m] b[m] = i } Buffer.prototype.swap16 = function swap16 () { var len = this.length if (len % 2 !== 0) { throw new RangeError('Buffer size must be a multiple of 16-bits') } for (var i = 0; i < len; i += 2) { swap(this, i, i + 1) } return this } Buffer.prototype.swap32 = function swap32 () { var len = this.length if (len % 4 !== 0) { throw new RangeError('Buffer size must be a multiple of 32-bits') } for (var i = 0; i < len; i += 4) { swap(this, i, i + 3) swap(this, i + 1, i + 2) } return this } Buffer.prototype.swap64 = function swap64 () { var len = this.length if (len % 8 !== 0) { throw new RangeError('Buffer size must be a multiple of 64-bits') } for (var i = 0; i < len; i += 8) { swap(this, i, i + 7) swap(this, i + 1, i + 6) swap(this, i + 2, i + 5) swap(this, i + 3, i + 4) } return this } Buffer.prototype.toString = function toString () { var length = this.length if (length === 0) return '' if (arguments.length === 0) return utf8Slice(this, 0, length) return slowToString.apply(this, arguments) } Buffer.prototype.equals = function equals (b) { if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') if (this === b) return true return Buffer.compare(this, b) === 0 } Buffer.prototype.inspect = function inspect () { var str = '' var max = exports.INSPECT_MAX_BYTES if (this.length > 0) { str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') if (this.length > max) str += ' ... ' } return '' } Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { if (!Buffer.isBuffer(target)) { throw new TypeError('Argument must be a Buffer') } if (start === undefined) { start = 0 } if (end === undefined) { end = target ? target.length : 0 } if (thisStart === undefined) { thisStart = 0 } if (thisEnd === undefined) { thisEnd = this.length } if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { throw new RangeError('out of range index') } if (thisStart >= thisEnd && start >= end) { return 0 } if (thisStart >= thisEnd) { return -1 } if (start >= end) { return 1 } start >>>= 0 end >>>= 0 thisStart >>>= 0 thisEnd >>>= 0 if (this === target) return 0 var x = thisEnd - thisStart var y = end - start var len = Math.min(x, y) var thisCopy = this.slice(thisStart, thisEnd) var targetCopy = target.slice(start, end) for (var i = 0; i < len; ++i) { if (thisCopy[i] !== targetCopy[i]) { x = thisCopy[i] y = targetCopy[i] break } } if (x < y) return -1 if (y < x) return 1 return 0 } // Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, // OR the last index of `val` in `buffer` at offset <= `byteOffset`. // // Arguments: // - buffer - a Buffer to search // - val - a string, Buffer, or number // - byteOffset - an index into `buffer`; will be clamped to an int32 // - encoding - an optional encoding, relevant is val is a string // - dir - true for indexOf, false for lastIndexOf function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { // Empty buffer means no match if (buffer.length === 0) return -1 // Normalize byteOffset if (typeof byteOffset === 'string') { encoding = byteOffset byteOffset = 0 } else if (byteOffset > 0x7fffffff) { byteOffset = 0x7fffffff } else if (byteOffset < -0x80000000) { byteOffset = -0x80000000 } byteOffset = +byteOffset // Coerce to Number. if (numberIsNaN(byteOffset)) { // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer byteOffset = dir ? 0 : (buffer.length - 1) } // Normalize byteOffset: negative offsets start from the end of the buffer if (byteOffset < 0) byteOffset = buffer.length + byteOffset if (byteOffset >= buffer.length) { if (dir) return -1 else byteOffset = buffer.length - 1 } else if (byteOffset < 0) { if (dir) byteOffset = 0 else return -1 } // Normalize val if (typeof val === 'string') { val = Buffer.from(val, encoding) } // Finally, search either indexOf (if dir is true) or lastIndexOf if (Buffer.isBuffer(val)) { // Special case: looking for empty string/buffer always fails if (val.length === 0) { return -1 } return arrayIndexOf(buffer, val, byteOffset, encoding, dir) } else if (typeof val === 'number') { val = val & 0xFF // Search for a byte value [0-255] if (typeof Uint8Array.prototype.indexOf === 'function') { if (dir) { return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) } else { return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) } } return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) } throw new TypeError('val must be string, number or Buffer') } function arrayIndexOf (arr, val, byteOffset, encoding, dir) { var indexSize = 1 var arrLength = arr.length var valLength = val.length if (encoding !== undefined) { encoding = String(encoding).toLowerCase() if (encoding === 'ucs2' || encoding === 'ucs-2' || encoding === 'utf16le' || encoding === 'utf-16le') { if (arr.length < 2 || val.length < 2) { return -1 } indexSize = 2 arrLength /= 2 valLength /= 2 byteOffset /= 2 } } function read (buf, i) { if (indexSize === 1) { return buf[i] } else { return buf.readUInt16BE(i * indexSize) } } var i if (dir) { var foundIndex = -1 for (i = byteOffset; i < arrLength; i++) { if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { if (foundIndex === -1) foundIndex = i if (i - foundIndex + 1 === valLength) return foundIndex * indexSize } else { if (foundIndex !== -1) i -= i - foundIndex foundIndex = -1 } } } else { if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength for (i = byteOffset; i >= 0; i--) { var found = true for (var j = 0; j < valLength; j++) { if (read(arr, i + j) !== read(val, j)) { found = false break } } if (found) return i } } return -1 } Buffer.prototype.includes = function includes (val, byteOffset, encoding) { return this.indexOf(val, byteOffset, encoding) !== -1 } Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { return bidirectionalIndexOf(this, val, byteOffset, encoding, true) } Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { return bidirectionalIndexOf(this, val, byteOffset, encoding, false) } function hexWrite (buf, string, offset, length) { offset = Number(offset) || 0 var remaining = buf.length - offset if (!length) { length = remaining } else { length = Number(length) if (length > remaining) { length = remaining } } // must be an even number of digits var strLen = string.length if (strLen % 2 !== 0) throw new TypeError('Invalid hex string') if (length > strLen / 2) { length = strLen / 2 } for (var i = 0; i < length; ++i) { var parsed = parseInt(string.substr(i * 2, 2), 16) if (numberIsNaN(parsed)) return i buf[offset + i] = parsed } return i } function utf8Write (buf, string, offset, length) { return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) } function asciiWrite (buf, string, offset, length) { return blitBuffer(asciiToBytes(string), buf, offset, length) } function latin1Write (buf, string, offset, length) { return asciiWrite(buf, string, offset, length) } function base64Write (buf, string, offset, length) { return blitBuffer(base64ToBytes(string), buf, offset, length) } function ucs2Write (buf, string, offset, length) { return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) } Buffer.prototype.write = function write (string, offset, length, encoding) { // Buffer#write(string) if (offset === undefined) { encoding = 'utf8' length = this.length offset = 0 // Buffer#write(string, encoding) } else if (length === undefined && typeof offset === 'string') { encoding = offset length = this.length offset = 0 // Buffer#write(string, offset[, length][, encoding]) } else if (isFinite(offset)) { offset = offset >>> 0 if (isFinite(length)) { length = length >>> 0 if (encoding === undefined) encoding = 'utf8' } else { encoding = length length = undefined } } else { throw new Error( 'Buffer.write(string, encoding, offset[, length]) is no longer supported' ) } var remaining = this.length - offset if (length === undefined || length > remaining) length = remaining if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { throw new RangeError('Attempt to write outside buffer bounds') } if (!encoding) encoding = 'utf8' var loweredCase = false for (;;) { switch (encoding) { case 'hex': return hexWrite(this, string, offset, length) case 'utf8': case 'utf-8': return utf8Write(this, string, offset, length) case 'ascii': return asciiWrite(this, string, offset, length) case 'latin1': case 'binary': return latin1Write(this, string, offset, length) case 'base64': // Warning: maxLength not taken into account in base64Write return base64Write(this, string, offset, length) case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return ucs2Write(this, string, offset, length) default: if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) encoding = ('' + encoding).toLowerCase() loweredCase = true } } } Buffer.prototype.toJSON = function toJSON () { return { type: 'Buffer', data: Array.prototype.slice.call(this._arr || this, 0) } } function base64Slice (buf, start, end) { if (start === 0 && end === buf.length) { return base64.fromByteArray(buf) } else { return base64.fromByteArray(buf.slice(start, end)) } } function utf8Slice (buf, start, end) { end = Math.min(buf.length, end) var res = [] var i = start while (i < end) { var firstByte = buf[i] var codePoint = null var bytesPerSequence = (firstByte > 0xEF) ? 4 : (firstByte > 0xDF) ? 3 : (firstByte > 0xBF) ? 2 : 1 if (i + bytesPerSequence <= end) { var secondByte, thirdByte, fourthByte, tempCodePoint switch (bytesPerSequence) { case 1: if (firstByte < 0x80) { codePoint = firstByte } break case 2: secondByte = buf[i + 1] if ((secondByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) if (tempCodePoint > 0x7F) { codePoint = tempCodePoint } } break case 3: secondByte = buf[i + 1] thirdByte = buf[i + 2] if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { codePoint = tempCodePoint } } break case 4: secondByte = buf[i + 1] thirdByte = buf[i + 2] fourthByte = buf[i + 3] if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { codePoint = tempCodePoint } } } } if (codePoint === null) { // we did not generate a valid codePoint so insert a // replacement char (U+FFFD) and advance only 1 byte codePoint = 0xFFFD bytesPerSequence = 1 } else if (codePoint > 0xFFFF) { // encode to utf16 (surrogate pair dance) codePoint -= 0x10000 res.push(codePoint >>> 10 & 0x3FF | 0xD800) codePoint = 0xDC00 | codePoint & 0x3FF } res.push(codePoint) i += bytesPerSequence } return decodeCodePointsArray(res) } // Based on http://stackoverflow.com/a/22747272/680742, the browser with // the lowest limit is Chrome, with 0x10000 args. // We go 1 magnitude less, for safety var MAX_ARGUMENTS_LENGTH = 0x1000 function decodeCodePointsArray (codePoints) { var len = codePoints.length if (len <= MAX_ARGUMENTS_LENGTH) { return String.fromCharCode.apply(String, codePoints) // avoid extra slice() } // Decode in chunks to avoid "call stack size exceeded". var res = '' var i = 0 while (i < len) { res += String.fromCharCode.apply( String, codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) ) } return res } function asciiSlice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) for (var i = start; i < end; ++i) { ret += String.fromCharCode(buf[i] & 0x7F) } return ret } function latin1Slice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) for (var i = start; i < end; ++i) { ret += String.fromCharCode(buf[i]) } return ret } function hexSlice (buf, start, end) { var len = buf.length if (!start || start < 0) start = 0 if (!end || end < 0 || end > len) end = len var out = '' for (var i = start; i < end; ++i) { out += toHex(buf[i]) } return out } function utf16leSlice (buf, start, end) { var bytes = buf.slice(start, end) var res = '' for (var i = 0; i < bytes.length; i += 2) { res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) } return res } Buffer.prototype.slice = function slice (start, end) { var len = this.length start = ~~start end = end === undefined ? len : ~~end if (start < 0) { start += len if (start < 0) start = 0 } else if (start > len) { start = len } if (end < 0) { end += len if (end < 0) end = 0 } else if (end > len) { end = len } if (end < start) end = start var newBuf = this.subarray(start, end) // Return an augmented `Uint8Array` instance newBuf.__proto__ = Buffer.prototype return newBuf } /* * Need to make sure that buffer isn't trying to write out of bounds. */ function checkOffset (offset, ext, length) { if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') } Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { offset = offset >>> 0 byteLength = byteLength >>> 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var val = this[offset] var mul = 1 var i = 0 while (++i < byteLength && (mul *= 0x100)) { val += this[offset + i] * mul } return val } Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { offset = offset >>> 0 byteLength = byteLength >>> 0 if (!noAssert) { checkOffset(offset, byteLength, this.length) } var val = this[offset + --byteLength] var mul = 1 while (byteLength > 0 && (mul *= 0x100)) { val += this[offset + --byteLength] * mul } return val } Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 1, this.length) return this[offset] } Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 2, this.length) return this[offset] | (this[offset + 1] << 8) } Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 2, this.length) return (this[offset] << 8) | this[offset + 1] } Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 4, this.length) return ((this[offset]) | (this[offset + 1] << 8) | (this[offset + 2] << 16)) + (this[offset + 3] * 0x1000000) } Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset] * 0x1000000) + ((this[offset + 1] << 16) | (this[offset + 2] << 8) | this[offset + 3]) } Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { offset = offset >>> 0 byteLength = byteLength >>> 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var val = this[offset] var mul = 1 var i = 0 while (++i < byteLength && (mul *= 0x100)) { val += this[offset + i] * mul } mul *= 0x80 if (val >= mul) val -= Math.pow(2, 8 * byteLength) return val } Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { offset = offset >>> 0 byteLength = byteLength >>> 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var i = byteLength var mul = 1 var val = this[offset + --i] while (i > 0 && (mul *= 0x100)) { val += this[offset + --i] * mul } mul *= 0x80 if (val >= mul) val -= Math.pow(2, 8 * byteLength) return val } Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 1, this.length) if (!(this[offset] & 0x80)) return (this[offset]) return ((0xff - this[offset] + 1) * -1) } Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 2, this.length) var val = this[offset] | (this[offset + 1] << 8) return (val & 0x8000) ? val | 0xFFFF0000 : val } Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 2, this.length) var val = this[offset + 1] | (this[offset] << 8) return (val & 0x8000) ? val | 0xFFFF0000 : val } Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset]) | (this[offset + 1] << 8) | (this[offset + 2] << 16) | (this[offset + 3] << 24) } Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset] << 24) | (this[offset + 1] << 16) | (this[offset + 2] << 8) | (this[offset + 3]) } Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 4, this.length) return ieee754.read(this, offset, true, 23, 4) } Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 4, this.length) return ieee754.read(this, offset, false, 23, 4) } Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 8, this.length) return ieee754.read(this, offset, true, 52, 8) } Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { offset = offset >>> 0 if (!noAssert) checkOffset(offset, 8, this.length) return ieee754.read(this, offset, false, 52, 8) } function checkInt (buf, value, offset, ext, max, min) { if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') if (offset + ext > buf.length) throw new RangeError('Index out of range') } Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset >>> 0 byteLength = byteLength >>> 0 if (!noAssert) { var maxBytes = Math.pow(2, 8 * byteLength) - 1 checkInt(this, value, offset, byteLength, maxBytes, 0) } var mul = 1 var i = 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { this[offset + i] = (value / mul) & 0xFF } return offset + byteLength } Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { value = +value offset = offset >>> 0 byteLength = byteLength >>> 0 if (!noAssert) { var maxBytes = Math.pow(2, 8 * byteLength) - 1 checkInt(this, value, offset, byteLength, maxBytes, 0) } var i = byteLength - 1 var mul = 1 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { this[offset + i] = (value / mul) & 0xFF } return offset + byteLength } Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) this[offset] = (value & 0xff) return offset + 1 } Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) return offset + 2 } Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) this[offset] = (value >>> 8) this[offset + 1] = (value & 0xff) return offset + 2 } Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) this[offset + 3] = (value >>> 24) this[offset + 2] = (value >>> 16) this[offset + 1] = (value >>> 8) this[offset] = (value & 0xff) return offset + 4 } Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) this[offset + 3] = (value & 0xff) return offset + 4 } Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) { var limit = Math.pow(2, (8 * byteLength) - 1) checkInt(this, value, offset, byteLength, limit - 1, -limit) } var i = 0 var mul = 1 var sub = 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { sub = 1 } this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } return offset + byteLength } Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) { var limit = Math.pow(2, (8 * byteLength) - 1) checkInt(this, value, offset, byteLength, limit - 1, -limit) } var i = byteLength - 1 var mul = 1 var sub = 0 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { sub = 1 } this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } return offset + byteLength } Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) if (value < 0) value = 0xff + value + 1 this[offset] = (value & 0xff) return offset + 1 } Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) return offset + 2 } Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) this[offset] = (value >>> 8) this[offset + 1] = (value & 0xff) return offset + 2 } Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) this[offset + 2] = (value >>> 16) this[offset + 3] = (value >>> 24) return offset + 4 } Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) if (value < 0) value = 0xffffffff + value + 1 this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) this[offset + 3] = (value & 0xff) return offset + 4 } function checkIEEE754 (buf, value, offset, ext, max, min) { if (offset + ext > buf.length) throw new RangeError('Index out of range') if (offset < 0) throw new RangeError('Index out of range') } function writeFloat (buf, value, offset, littleEndian, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) { checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) } ieee754.write(buf, value, offset, littleEndian, 23, 4) return offset + 4 } Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { return writeFloat(this, value, offset, true, noAssert) } Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { return writeFloat(this, value, offset, false, noAssert) } function writeDouble (buf, value, offset, littleEndian, noAssert) { value = +value offset = offset >>> 0 if (!noAssert) { checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) } ieee754.write(buf, value, offset, littleEndian, 52, 8) return offset + 8 } Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { return writeDouble(this, value, offset, true, noAssert) } Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { return writeDouble(this, value, offset, false, noAssert) } // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) Buffer.prototype.copy = function copy (target, targetStart, start, end) { if (!start) start = 0 if (!end && end !== 0) end = this.length if (targetStart >= target.length) targetStart = target.length if (!targetStart) targetStart = 0 if (end > 0 && end < start) end = start // Copy 0 bytes; we're done if (end === start) return 0 if (target.length === 0 || this.length === 0) return 0 // Fatal error conditions if (targetStart < 0) { throw new RangeError('targetStart out of bounds') } if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') if (end < 0) throw new RangeError('sourceEnd out of bounds') // Are we oob? if (end > this.length) end = this.length if (target.length - targetStart < end - start) { end = target.length - targetStart + start } var len = end - start var i if (this === target && start < targetStart && targetStart < end) { // descending copy from end for (i = len - 1; i >= 0; --i) { target[i + targetStart] = this[i + start] } } else if (len < 1000) { // ascending copy from start for (i = 0; i < len; ++i) { target[i + targetStart] = this[i + start] } } else { Uint8Array.prototype.set.call( target, this.subarray(start, start + len), targetStart ) } return len } // Usage: // buffer.fill(number[, offset[, end]]) // buffer.fill(buffer[, offset[, end]]) // buffer.fill(string[, offset[, end]][, encoding]) Buffer.prototype.fill = function fill (val, start, end, encoding) { // Handle string cases: if (typeof val === 'string') { if (typeof start === 'string') { encoding = start start = 0 end = this.length } else if (typeof end === 'string') { encoding = end end = this.length } if (val.length === 1) { var code = val.charCodeAt(0) if (code < 256) { val = code } } if (encoding !== undefined && typeof encoding !== 'string') { throw new TypeError('encoding must be a string') } if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { throw new TypeError('Unknown encoding: ' + encoding) } } else if (typeof val === 'number') { val = val & 255 } // Invalid ranges are not set to a default, so can range check early. if (start < 0 || this.length < start || this.length < end) { throw new RangeError('Out of range index') } if (end <= start) { return this } start = start >>> 0 end = end === undefined ? this.length : end >>> 0 if (!val) val = 0 var i if (typeof val === 'number') { for (i = start; i < end; ++i) { this[i] = val } } else { var bytes = Buffer.isBuffer(val) ? val : new Buffer(val, encoding) var len = bytes.length for (i = 0; i < end - start; ++i) { this[i + start] = bytes[i % len] } } return this } // HELPER FUNCTIONS // ================ var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g function base64clean (str) { // Node strips out invalid characters like \n and \t from the string, base64-js does not str = str.trim().replace(INVALID_BASE64_RE, '') // Node converts strings with length < 2 to '' if (str.length < 2) return '' // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not while (str.length % 4 !== 0) { str = str + '=' } return str } function toHex (n) { if (n < 16) return '0' + n.toString(16) return n.toString(16) } function utf8ToBytes (string, units) { units = units || Infinity var codePoint var length = string.length var leadSurrogate = null var bytes = [] for (var i = 0; i < length; ++i) { codePoint = string.charCodeAt(i) // is surrogate component if (codePoint > 0xD7FF && codePoint < 0xE000) { // last char was a lead if (!leadSurrogate) { // no lead yet if (codePoint > 0xDBFF) { // unexpected trail if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue } else if (i + 1 === length) { // unpaired lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue } // valid lead leadSurrogate = codePoint continue } // 2 leads in a row if (codePoint < 0xDC00) { if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) leadSurrogate = codePoint continue } // valid surrogate pair codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 } else if (leadSurrogate) { // valid bmp char, but last char was a lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) } leadSurrogate = null // encode utf8 if (codePoint < 0x80) { if ((units -= 1) < 0) break bytes.push(codePoint) } else if (codePoint < 0x800) { if ((units -= 2) < 0) break bytes.push( codePoint >> 0x6 | 0xC0, codePoint & 0x3F | 0x80 ) } else if (codePoint < 0x10000) { if ((units -= 3) < 0) break bytes.push( codePoint >> 0xC | 0xE0, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) } else if (codePoint < 0x110000) { if ((units -= 4) < 0) break bytes.push( codePoint >> 0x12 | 0xF0, codePoint >> 0xC & 0x3F | 0x80, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) } else { throw new Error('Invalid code point') } } return bytes } function asciiToBytes (str) { var byteArray = [] for (var i = 0; i < str.length; ++i) { // Node's code seems to be doing this and not & 0x7F.. byteArray.push(str.charCodeAt(i) & 0xFF) } return byteArray } function utf16leToBytes (str, units) { var c, hi, lo var byteArray = [] for (var i = 0; i < str.length; ++i) { if ((units -= 2) < 0) break c = str.charCodeAt(i) hi = c >> 8 lo = c % 256 byteArray.push(lo) byteArray.push(hi) } return byteArray } function base64ToBytes (str) { return base64.toByteArray(base64clean(str)) } function blitBuffer (src, dst, offset, length) { for (var i = 0; i < length; ++i) { if ((i + offset >= dst.length) || (i >= src.length)) break dst[i + offset] = src[i] } return i } // ArrayBuffers from another context (i.e. an iframe) do not pass the `instanceof` check // but they should be treated as valid. See: https://github.com/feross/buffer/issues/166 function isArrayBuffer (obj) { return obj instanceof ArrayBuffer || (obj != null && obj.constructor != null && obj.constructor.name === 'ArrayBuffer' && typeof obj.byteLength === 'number') } // Node 0.10 supports `ArrayBuffer` but lacks `ArrayBuffer.isView` function isArrayBufferView (obj) { return (typeof ArrayBuffer.isView === 'function') && ArrayBuffer.isView(obj) } function numberIsNaN (obj) { return obj !== obj // eslint-disable-line no-self-compare } },{"base64-js":174,"ieee754":184}],179:[function(require,module,exports){ module.exports = { "100": "Continue", "101": "Switching Protocols", "102": "Processing", "200": "OK", "201": "Created", "202": "Accepted", "203": "Non-Authoritative Information", "204": "No Content", "205": "Reset Content", "206": "Partial Content", "207": "Multi-Status", "208": "Already Reported", "226": "IM Used", "300": "Multiple Choices", "301": "Moved Permanently", "302": "Found", "303": "See Other", "304": "Not Modified", "305": "Use Proxy", "307": "Temporary Redirect", "308": "Permanent Redirect", "400": "Bad Request", "401": "Unauthorized", "402": "Payment Required", "403": "Forbidden", "404": "Not Found", "405": "Method Not Allowed", "406": "Not Acceptable", "407": "Proxy Authentication Required", "408": "Request Timeout", "409": "Conflict", "410": "Gone", "411": "Length Required", "412": "Precondition Failed", "413": "Payload Too Large", "414": "URI Too Long", "415": "Unsupported Media Type", "416": "Range Not Satisfiable", "417": "Expectation Failed", "418": "I'm a teapot", "421": "Misdirected Request", "422": "Unprocessable Entity", "423": "Locked", "424": "Failed Dependency", "425": "Unordered Collection", "426": "Upgrade Required", "428": "Precondition Required", "429": "Too Many Requests", "431": "Request Header Fields Too Large", "451": "Unavailable For Legal Reasons", "500": "Internal Server Error", "501": "Not Implemented", "502": "Bad Gateway", "503": "Service Unavailable", "504": "Gateway Timeout", "505": "HTTP Version Not Supported", "506": "Variant Also Negotiates", "507": "Insufficient Storage", "508": "Loop Detected", "509": "Bandwidth Limit Exceeded", "510": "Not Extended", "511": "Network Authentication Required" } },{}],180:[function(require,module,exports){ module.exports={ "O_RDONLY": 0, "O_WRONLY": 1, "O_RDWR": 2, "S_IFMT": 61440, "S_IFREG": 32768, "S_IFDIR": 16384, "S_IFCHR": 8192, "S_IFBLK": 24576, "S_IFIFO": 4096, "S_IFLNK": 40960, "S_IFSOCK": 49152, "O_CREAT": 512, "O_EXCL": 2048, "O_NOCTTY": 131072, "O_TRUNC": 1024, "O_APPEND": 8, "O_DIRECTORY": 1048576, "O_NOFOLLOW": 256, "O_SYNC": 128, "O_SYMLINK": 2097152, "O_NONBLOCK": 4, "S_IRWXU": 448, "S_IRUSR": 256, "S_IWUSR": 128, "S_IXUSR": 64, "S_IRWXG": 56, "S_IRGRP": 32, "S_IWGRP": 16, "S_IXGRP": 8, "S_IRWXO": 7, "S_IROTH": 4, "S_IWOTH": 2, "S_IXOTH": 1, "E2BIG": 7, "EACCES": 13, "EADDRINUSE": 48, "EADDRNOTAVAIL": 49, "EAFNOSUPPORT": 47, "EAGAIN": 35, "EALREADY": 37, "EBADF": 9, "EBADMSG": 94, "EBUSY": 16, "ECANCELED": 89, "ECHILD": 10, "ECONNABORTED": 53, "ECONNREFUSED": 61, "ECONNRESET": 54, "EDEADLK": 11, "EDESTADDRREQ": 39, "EDOM": 33, "EDQUOT": 69, "EEXIST": 17, "EFAULT": 14, "EFBIG": 27, "EHOSTUNREACH": 65, "EIDRM": 90, "EILSEQ": 92, "EINPROGRESS": 36, "EINTR": 4, "EINVAL": 22, "EIO": 5, "EISCONN": 56, "EISDIR": 21, "ELOOP": 62, "EMFILE": 24, "EMLINK": 31, "EMSGSIZE": 40, "EMULTIHOP": 95, "ENAMETOOLONG": 63, "ENETDOWN": 50, "ENETRESET": 52, "ENETUNREACH": 51, "ENFILE": 23, "ENOBUFS": 55, "ENODATA": 96, "ENODEV": 19, "ENOENT": 2, "ENOEXEC": 8, "ENOLCK": 77, "ENOLINK": 97, "ENOMEM": 12, "ENOMSG": 91, "ENOPROTOOPT": 42, "ENOSPC": 28, "ENOSR": 98, "ENOSTR": 99, "ENOSYS": 78, "ENOTCONN": 57, "ENOTDIR": 20, "ENOTEMPTY": 66, "ENOTSOCK": 38, "ENOTSUP": 45, "ENOTTY": 25, "ENXIO": 6, "EOPNOTSUPP": 102, "EOVERFLOW": 84, "EPERM": 1, "EPIPE": 32, "EPROTO": 100, "EPROTONOSUPPORT": 43, "EPROTOTYPE": 41, "ERANGE": 34, "EROFS": 30, "ESPIPE": 29, "ESRCH": 3, "ESTALE": 70, "ETIME": 101, "ETIMEDOUT": 60, "ETXTBSY": 26, "EWOULDBLOCK": 35, "EXDEV": 18, "SIGHUP": 1, "SIGINT": 2, "SIGQUIT": 3, "SIGILL": 4, "SIGTRAP": 5, "SIGABRT": 6, "SIGIOT": 6, "SIGBUS": 10, "SIGFPE": 8, "SIGKILL": 9, "SIGUSR1": 30, "SIGSEGV": 11, "SIGUSR2": 31, "SIGPIPE": 13, "SIGALRM": 14, "SIGTERM": 15, "SIGCHLD": 20, "SIGCONT": 19, "SIGSTOP": 17, "SIGTSTP": 18, "SIGTTIN": 21, "SIGTTOU": 22, "SIGURG": 16, "SIGXCPU": 24, "SIGXFSZ": 25, "SIGVTALRM": 26, "SIGPROF": 27, "SIGWINCH": 28, "SIGIO": 23, "SIGSYS": 12, "SSL_OP_ALL": 2147486719, "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION": 262144, "SSL_OP_CIPHER_SERVER_PREFERENCE": 4194304, "SSL_OP_CISCO_ANYCONNECT": 32768, "SSL_OP_COOKIE_EXCHANGE": 8192, "SSL_OP_CRYPTOPRO_TLSEXT_BUG": 2147483648, "SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS": 2048, "SSL_OP_EPHEMERAL_RSA": 0, "SSL_OP_LEGACY_SERVER_CONNECT": 4, "SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER": 32, "SSL_OP_MICROSOFT_SESS_ID_BUG": 1, "SSL_OP_MSIE_SSLV2_RSA_PADDING": 0, "SSL_OP_NETSCAPE_CA_DN_BUG": 536870912, "SSL_OP_NETSCAPE_CHALLENGE_BUG": 2, "SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG": 1073741824, "SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG": 8, "SSL_OP_NO_COMPRESSION": 131072, "SSL_OP_NO_QUERY_MTU": 4096, "SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION": 65536, "SSL_OP_NO_SSLv2": 16777216, "SSL_OP_NO_SSLv3": 33554432, "SSL_OP_NO_TICKET": 16384, "SSL_OP_NO_TLSv1": 67108864, "SSL_OP_NO_TLSv1_1": 268435456, "SSL_OP_NO_TLSv1_2": 134217728, "SSL_OP_PKCS1_CHECK_1": 0, "SSL_OP_PKCS1_CHECK_2": 0, "SSL_OP_SINGLE_DH_USE": 1048576, "SSL_OP_SINGLE_ECDH_USE": 524288, "SSL_OP_SSLEAY_080_CLIENT_DH_BUG": 128, "SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG": 0, "SSL_OP_TLS_BLOCK_PADDING_BUG": 512, "SSL_OP_TLS_D5_BUG": 256, "SSL_OP_TLS_ROLLBACK_BUG": 8388608, "ENGINE_METHOD_DSA": 2, "ENGINE_METHOD_DH": 4, "ENGINE_METHOD_RAND": 8, "ENGINE_METHOD_ECDH": 16, "ENGINE_METHOD_ECDSA": 32, "ENGINE_METHOD_CIPHERS": 64, "ENGINE_METHOD_DIGESTS": 128, "ENGINE_METHOD_STORE": 256, "ENGINE_METHOD_PKEY_METHS": 512, "ENGINE_METHOD_PKEY_ASN1_METHS": 1024, "ENGINE_METHOD_ALL": 65535, "ENGINE_METHOD_NONE": 0, "DH_CHECK_P_NOT_SAFE_PRIME": 2, "DH_CHECK_P_NOT_PRIME": 1, "DH_UNABLE_TO_CHECK_GENERATOR": 4, "DH_NOT_SUITABLE_GENERATOR": 8, "NPN_ENABLED": 1, "RSA_PKCS1_PADDING": 1, "RSA_SSLV23_PADDING": 2, "RSA_NO_PADDING": 3, "RSA_PKCS1_OAEP_PADDING": 4, "RSA_X931_PADDING": 5, "RSA_PKCS1_PSS_PADDING": 6, "POINT_CONVERSION_COMPRESSED": 2, "POINT_CONVERSION_UNCOMPRESSED": 4, "POINT_CONVERSION_HYBRID": 6, "F_OK": 0, "R_OK": 4, "W_OK": 2, "X_OK": 1, "UV_UDP_REUSEADDR": 4 } },{}],181:[function(require,module,exports){ (function (Buffer){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // NOTE: These type checking functions intentionally don't use `instanceof` // because it is fragile and can be easily faked with `Object.create()`. function isArray(arg) { if (Array.isArray) { return Array.isArray(arg); } return objectToString(arg) === '[object Array]'; } exports.isArray = isArray; function isBoolean(arg) { return typeof arg === 'boolean'; } exports.isBoolean = isBoolean; function isNull(arg) { return arg === null; } exports.isNull = isNull; function isNullOrUndefined(arg) { return arg == null; } exports.isNullOrUndefined = isNullOrUndefined; function isNumber(arg) { return typeof arg === 'number'; } exports.isNumber = isNumber; function isString(arg) { return typeof arg === 'string'; } exports.isString = isString; function isSymbol(arg) { return typeof arg === 'symbol'; } exports.isSymbol = isSymbol; function isUndefined(arg) { return arg === void 0; } exports.isUndefined = isUndefined; function isRegExp(re) { return objectToString(re) === '[object RegExp]'; } exports.isRegExp = isRegExp; function isObject(arg) { return typeof arg === 'object' && arg !== null; } exports.isObject = isObject; function isDate(d) { return objectToString(d) === '[object Date]'; } exports.isDate = isDate; function isError(e) { return (objectToString(e) === '[object Error]' || e instanceof Error); } exports.isError = isError; function isFunction(arg) { return typeof arg === 'function'; } exports.isFunction = isFunction; function isPrimitive(arg) { return arg === null || typeof arg === 'boolean' || typeof arg === 'number' || typeof arg === 'string' || typeof arg === 'symbol' || // ES6 symbol typeof arg === 'undefined'; } exports.isPrimitive = isPrimitive; exports.isBuffer = Buffer.isBuffer; function objectToString(o) { return Object.prototype.toString.call(o); } }).call(this,{"isBuffer":require("../../is-buffer/index.js")}) },{"../../is-buffer/index.js":186}],182:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined; } module.exports = EventEmitter; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. EventEmitter.defaultMaxListeners = 10; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function(n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: args = Array.prototype.slice.call(arguments, 1); handler.apply(this, args); } } else if (isObject(handler)) { args = Array.prototype.slice.call(arguments, 1); listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; else if (isObject(this._events[type])) // If we've already got an array, just append. this._events[type].push(listener); else // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // Check for listener leak if (isObject(this._events[type]) && !this._events[type].warned) { if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in IE 10 console.trace(); } } } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removeListener' event iff the listener was removed EventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type, listener); } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else if (listeners) { // LIFO order while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; EventEmitter.prototype.listenerCount = function(type) { if (this._events) { var evlistener = this._events[type]; if (isFunction(evlistener)) return 1; else if (evlistener) return evlistener.length; } return 0; }; EventEmitter.listenerCount = function(emitter, type) { return emitter.listenerCount(type); }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } },{}],183:[function(require,module,exports){ var http = require('http') var url = require('url') var https = module.exports for (var key in http) { if (http.hasOwnProperty(key)) https[key] = http[key] } https.request = function (params, cb) { params = validateParams(params) return http.request.call(this, params, cb) } https.get = function (params, cb) { params = validateParams(params) return http.get.call(this, params, cb) } function validateParams (params) { if (typeof params === 'string') { params = url.parse(params) } if (!params.protocol) { params.protocol = 'https:' } if (params.protocol !== 'https:') { throw new Error('Protocol "' + params.protocol + '" not supported. Expected "https:"') } return params } },{"http":220,"url":226}],184:[function(require,module,exports){ exports.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m var eLen = nBytes * 8 - mLen - 1 var eMax = (1 << eLen) - 1 var eBias = eMax >> 1 var nBits = -7 var i = isLE ? (nBytes - 1) : 0 var d = isLE ? -1 : 1 var s = buffer[offset + i] i += d e = s & ((1 << (-nBits)) - 1) s >>= (-nBits) nBits += eLen for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} m = e & ((1 << (-nBits)) - 1) e >>= (-nBits) nBits += mLen for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} if (e === 0) { e = 1 - eBias } else if (e === eMax) { return m ? NaN : ((s ? -1 : 1) * Infinity) } else { m = m + Math.pow(2, mLen) e = e - eBias } return (s ? -1 : 1) * m * Math.pow(2, e - mLen) } exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { var e, m, c var eLen = nBytes * 8 - mLen - 1 var eMax = (1 << eLen) - 1 var eBias = eMax >> 1 var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) var i = isLE ? 0 : (nBytes - 1) var d = isLE ? 1 : -1 var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 value = Math.abs(value) if (isNaN(value) || value === Infinity) { m = isNaN(value) ? 1 : 0 e = eMax } else { e = Math.floor(Math.log(value) / Math.LN2) if (value * (c = Math.pow(2, -e)) < 1) { e-- c *= 2 } if (e + eBias >= 1) { value += rt / c } else { value += rt * Math.pow(2, 1 - eBias) } if (value * c >= 2) { e++ c /= 2 } if (e + eBias >= eMax) { m = 0 e = eMax } else if (e + eBias >= 1) { m = (value * c - 1) * Math.pow(2, mLen) e = e + eBias } else { m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) e = 0 } } for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} e = (e << mLen) | m eLen += mLen for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} buffer[offset + i - d] |= s * 128 } },{}],185:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { ctor.super_ = superCtor ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; } else { // old school shim for old browsers module.exports = function inherits(ctor, superCtor) { ctor.super_ = superCtor var TempCtor = function () {} TempCtor.prototype = superCtor.prototype ctor.prototype = new TempCtor() ctor.prototype.constructor = ctor } } },{}],186:[function(require,module,exports){ /*! * Determine if an object is a Buffer * * @author Feross Aboukhadijeh * @license MIT */ // The _isBuffer check is for Safari 5-7 support, because it's missing // Object.prototype.constructor. Remove this eventually module.exports = function (obj) { return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) } function isBuffer (obj) { return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) } // For Node v0.10 support. Remove this eventually. function isSlowBuffer (obj) { return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) } },{}],187:[function(require,module,exports){ var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; },{}],188:[function(require,module,exports){ 'use strict'; var TYPED_OK = (typeof Uint8Array !== 'undefined') && (typeof Uint16Array !== 'undefined') && (typeof Int32Array !== 'undefined'); function _has(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); } exports.assign = function (obj /*from1, from2, from3, ...*/) { var sources = Array.prototype.slice.call(arguments, 1); while (sources.length) { var source = sources.shift(); if (!source) { continue; } if (typeof source !== 'object') { throw new TypeError(source + 'must be non-object'); } for (var p in source) { if (_has(source, p)) { obj[p] = source[p]; } } } return obj; }; // reduce buffer size, avoiding mem copy exports.shrinkBuf = function (buf, size) { if (buf.length === size) { return buf; } if (buf.subarray) { return buf.subarray(0, size); } buf.length = size; return buf; }; var fnTyped = { arraySet: function (dest, src, src_offs, len, dest_offs) { if (src.subarray && dest.subarray) { dest.set(src.subarray(src_offs, src_offs + len), dest_offs); return; } // Fallback to ordinary array for (var i = 0; i < len; i++) { dest[dest_offs + i] = src[src_offs + i]; } }, // Join array of chunks to single array. flattenChunks: function (chunks) { var i, l, len, pos, chunk, result; // calculate data length len = 0; for (i = 0, l = chunks.length; i < l; i++) { len += chunks[i].length; } // join chunks result = new Uint8Array(len); pos = 0; for (i = 0, l = chunks.length; i < l; i++) { chunk = chunks[i]; result.set(chunk, pos); pos += chunk.length; } return result; } }; var fnUntyped = { arraySet: function (dest, src, src_offs, len, dest_offs) { for (var i = 0; i < len; i++) { dest[dest_offs + i] = src[src_offs + i]; } }, // Join array of chunks to single array. flattenChunks: function (chunks) { return [].concat.apply([], chunks); } }; // Enable/Disable typed arrays use, for testing // exports.setTyped = function (on) { if (on) { exports.Buf8 = Uint8Array; exports.Buf16 = Uint16Array; exports.Buf32 = Int32Array; exports.assign(exports, fnTyped); } else { exports.Buf8 = Array; exports.Buf16 = Array; exports.Buf32 = Array; exports.assign(exports, fnUntyped); } }; exports.setTyped(TYPED_OK); },{}],189:[function(require,module,exports){ 'use strict'; // Note: adler32 takes 12% for level 0 and 2% for level 6. // It isn't worth it to make additional optimizations as in original. // Small size is preferable. // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. function adler32(adler, buf, len, pos) { var s1 = (adler & 0xffff) |0, s2 = ((adler >>> 16) & 0xffff) |0, n = 0; while (len !== 0) { // Set limit ~ twice less than 5552, to keep // s2 in 31-bits, because we force signed ints. // in other case %= will fail. n = len > 2000 ? 2000 : len; len -= n; do { s1 = (s1 + buf[pos++]) |0; s2 = (s2 + s1) |0; } while (--n); s1 %= 65521; s2 %= 65521; } return (s1 | (s2 << 16)) |0; } module.exports = adler32; },{}],190:[function(require,module,exports){ 'use strict'; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. module.exports = { /* Allowed flush values; see deflate() and inflate() below for details */ Z_NO_FLUSH: 0, Z_PARTIAL_FLUSH: 1, Z_SYNC_FLUSH: 2, Z_FULL_FLUSH: 3, Z_FINISH: 4, Z_BLOCK: 5, Z_TREES: 6, /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ Z_OK: 0, Z_STREAM_END: 1, Z_NEED_DICT: 2, Z_ERRNO: -1, Z_STREAM_ERROR: -2, Z_DATA_ERROR: -3, //Z_MEM_ERROR: -4, Z_BUF_ERROR: -5, //Z_VERSION_ERROR: -6, /* compression levels */ Z_NO_COMPRESSION: 0, Z_BEST_SPEED: 1, Z_BEST_COMPRESSION: 9, Z_DEFAULT_COMPRESSION: -1, Z_FILTERED: 1, Z_HUFFMAN_ONLY: 2, Z_RLE: 3, Z_FIXED: 4, Z_DEFAULT_STRATEGY: 0, /* Possible values of the data_type field (though see inflate()) */ Z_BINARY: 0, Z_TEXT: 1, //Z_ASCII: 1, // = Z_TEXT (deprecated) Z_UNKNOWN: 2, /* The deflate compression method */ Z_DEFLATED: 8 //Z_NULL: null // Use -1 or null inline, depending on var type }; },{}],191:[function(require,module,exports){ 'use strict'; // Note: we can't get significant speed boost here. // So write code to minimize size - no pregenerated tables // and array tools dependencies. // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // Use ordinary array, since untyped makes no boost here function makeTable() { var c, table = []; for (var n = 0; n < 256; n++) { c = n; for (var k = 0; k < 8; k++) { c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); } table[n] = c; } return table; } // Create table on load. Just 255 signed longs. Not a problem. var crcTable = makeTable(); function crc32(crc, buf, len, pos) { var t = crcTable, end = pos + len; crc ^= -1; for (var i = pos; i < end; i++) { crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; } return (crc ^ (-1)); // >>> 0; } module.exports = crc32; },{}],192:[function(require,module,exports){ 'use strict'; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. var utils = require('../utils/common'); var trees = require('./trees'); var adler32 = require('./adler32'); var crc32 = require('./crc32'); var msg = require('./messages'); /* Public constants ==========================================================*/ /* ===========================================================================*/ /* Allowed flush values; see deflate() and inflate() below for details */ var Z_NO_FLUSH = 0; var Z_PARTIAL_FLUSH = 1; //var Z_SYNC_FLUSH = 2; var Z_FULL_FLUSH = 3; var Z_FINISH = 4; var Z_BLOCK = 5; //var Z_TREES = 6; /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ var Z_OK = 0; var Z_STREAM_END = 1; //var Z_NEED_DICT = 2; //var Z_ERRNO = -1; var Z_STREAM_ERROR = -2; var Z_DATA_ERROR = -3; //var Z_MEM_ERROR = -4; var Z_BUF_ERROR = -5; //var Z_VERSION_ERROR = -6; /* compression levels */ //var Z_NO_COMPRESSION = 0; //var Z_BEST_SPEED = 1; //var Z_BEST_COMPRESSION = 9; var Z_DEFAULT_COMPRESSION = -1; var Z_FILTERED = 1; var Z_HUFFMAN_ONLY = 2; var Z_RLE = 3; var Z_FIXED = 4; var Z_DEFAULT_STRATEGY = 0; /* Possible values of the data_type field (though see inflate()) */ //var Z_BINARY = 0; //var Z_TEXT = 1; //var Z_ASCII = 1; // = Z_TEXT var Z_UNKNOWN = 2; /* The deflate compression method */ var Z_DEFLATED = 8; /*============================================================================*/ var MAX_MEM_LEVEL = 9; /* Maximum value for memLevel in deflateInit2 */ var MAX_WBITS = 15; /* 32K LZ77 window */ var DEF_MEM_LEVEL = 8; var LENGTH_CODES = 29; /* number of length codes, not counting the special END_BLOCK code */ var LITERALS = 256; /* number of literal bytes 0..255 */ var L_CODES = LITERALS + 1 + LENGTH_CODES; /* number of Literal or Length codes, including the END_BLOCK code */ var D_CODES = 30; /* number of distance codes */ var BL_CODES = 19; /* number of codes used to transfer the bit lengths */ var HEAP_SIZE = 2 * L_CODES + 1; /* maximum heap size */ var MAX_BITS = 15; /* All codes must not exceed MAX_BITS bits */ var MIN_MATCH = 3; var MAX_MATCH = 258; var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); var PRESET_DICT = 0x20; var INIT_STATE = 42; var EXTRA_STATE = 69; var NAME_STATE = 73; var COMMENT_STATE = 91; var HCRC_STATE = 103; var BUSY_STATE = 113; var FINISH_STATE = 666; var BS_NEED_MORE = 1; /* block not completed, need more input or more output */ var BS_BLOCK_DONE = 2; /* block flush performed */ var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ var OS_CODE = 0x03; // Unix :) . Don't detect, use this default. function err(strm, errorCode) { strm.msg = msg[errorCode]; return errorCode; } function rank(f) { return ((f) << 1) - ((f) > 4 ? 9 : 0); } function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } } /* ========================================================================= * Flush as much pending output as possible. All deflate() output goes * through this function so some applications may wish to modify it * to avoid allocating a large strm->output buffer and copying into it. * (See also read_buf()). */ function flush_pending(strm) { var s = strm.state; //_tr_flush_bits(s); var len = s.pending; if (len > strm.avail_out) { len = strm.avail_out; } if (len === 0) { return; } utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out); strm.next_out += len; s.pending_out += len; strm.total_out += len; strm.avail_out -= len; s.pending -= len; if (s.pending === 0) { s.pending_out = 0; } } function flush_block_only(s, last) { trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); s.block_start = s.strstart; flush_pending(s.strm); } function put_byte(s, b) { s.pending_buf[s.pending++] = b; } /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ function putShortMSB(s, b) { // put_byte(s, (Byte)(b >> 8)); // put_byte(s, (Byte)(b & 0xff)); s.pending_buf[s.pending++] = (b >>> 8) & 0xff; s.pending_buf[s.pending++] = b & 0xff; } /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->input buffer and copying from it. * (See also flush_pending()). */ function read_buf(strm, buf, start, size) { var len = strm.avail_in; if (len > size) { len = size; } if (len === 0) { return 0; } strm.avail_in -= len; // zmemcpy(buf, strm->next_in, len); utils.arraySet(buf, strm.input, strm.next_in, len, start); if (strm.state.wrap === 1) { strm.adler = adler32(strm.adler, buf, len, start); } else if (strm.state.wrap === 2) { strm.adler = crc32(strm.adler, buf, len, start); } strm.next_in += len; strm.total_in += len; return len; } /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ function longest_match(s, cur_match) { var chain_length = s.max_chain_length; /* max hash chain length */ var scan = s.strstart; /* current string */ var match; /* matched string */ var len; /* length of current match */ var best_len = s.prev_length; /* best match length so far */ var nice_match = s.nice_match; /* stop if match long enough */ var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; var _win = s.window; // shortcut var wmask = s.w_mask; var prev = s.prev; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ var strend = s.strstart + MAX_MATCH; var scan_end1 = _win[scan + best_len - 1]; var scan_end = _win[scan + best_len]; /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s.prev_length >= s.good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if (nice_match > s.lookahead) { nice_match = s.lookahead; } // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { // Assert(cur_match < s->strstart, "no future"); match = cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below * for insufficient lookahead only occur occasionally for performance * reasons. Therefore uninitialized memory will be accessed, and * conditional jumps will be made that depend on those values. * However the length of the match is limited to the lookahead, so * the output of deflate is not affected by the uninitialized values. */ if (_win[match + best_len] !== scan_end || _win[match + best_len - 1] !== scan_end1 || _win[match] !== _win[scan] || _win[++match] !== _win[scan + 1]) { continue; } /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2; match++; // Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { /*jshint noempty:false*/ } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && scan < strend); // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (strend - scan); scan = strend - MAX_MATCH; if (len > best_len) { s.match_start = cur_match; best_len = len; if (len >= nice_match) { break; } scan_end1 = _win[scan + best_len - 1]; scan_end = _win[scan + best_len]; } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); if (best_len <= s.lookahead) { return best_len; } return s.lookahead; } /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ function fill_window(s) { var _w_size = s.w_size; var p, n, m, more, str; //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); do { more = s.window_size - s.lookahead - s.strstart; // JS ints have 32 bit, block below not needed /* Deal with !@#$% 64K limit: */ //if (sizeof(int) <= 2) { // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { // more = wsize; // // } else if (more == (unsigned)(-1)) { // /* Very unlikely, but possible on 16 bit machine if // * strstart == 0 && lookahead == 1 (input done a byte at time) // */ // more--; // } //} /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { utils.arraySet(s.window, s.window, _w_size, _w_size, 0); s.match_start -= _w_size; s.strstart -= _w_size; /* we now have strstart >= MAX_DIST */ s.block_start -= _w_size; /* Slide the hash table (could be avoided with 32 bit values at the expense of memory usage). We slide even when level == 0 to keep the hash table consistent if we switch back to level > 0 later. (Using level 0 permanently is not an optimal usage of zlib, so we don't care about this pathological case.) */ n = s.hash_size; p = n; do { m = s.head[--p]; s.head[p] = (m >= _w_size ? m - _w_size : 0); } while (--n); n = _w_size; p = n; do { m = s.prev[--p]; s.prev[p] = (m >= _w_size ? m - _w_size : 0); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); more += _w_size; } if (s.strm.avail_in === 0) { break; } /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ //Assert(more >= 2, "more < 2"); n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); s.lookahead += n; /* Initialize the hash value now that we have some input: */ if (s.lookahead + s.insert >= MIN_MATCH) { str = s.strstart - s.insert; s.ins_h = s.window[str]; /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask; //#if MIN_MATCH != 3 // Call update_hash() MIN_MATCH-3 more times //#endif while (s.insert) { /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; s.prev[str & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = str; str++; s.insert--; if (s.lookahead + s.insert < MIN_MATCH) { break; } } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); /* If the WIN_INIT bytes after the end of the current data have never been * written, then zero those bytes in order to avoid memory check reports of * the use of uninitialized (or uninitialised as Julian writes) bytes by * the longest match routines. Update the high water mark for the next * time through here. WIN_INIT is set to MAX_MATCH since the longest match * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. */ // if (s.high_water < s.window_size) { // var curr = s.strstart + s.lookahead; // var init = 0; // // if (s.high_water < curr) { // /* Previous high water mark below current data -- zero WIN_INIT // * bytes or up to end of window, whichever is less. // */ // init = s.window_size - curr; // if (init > WIN_INIT) // init = WIN_INIT; // zmemzero(s->window + curr, (unsigned)init); // s->high_water = curr + init; // } // else if (s->high_water < (ulg)curr + WIN_INIT) { // /* High water mark at or above current data, but below current data // * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up // * to end of window, whichever is less. // */ // init = (ulg)curr + WIN_INIT - s->high_water; // if (init > s->window_size - s->high_water) // init = s->window_size - s->high_water; // zmemzero(s->window + s->high_water, (unsigned)init); // s->high_water += init; // } // } // // Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, // "not enough room for search"); } /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * This function does not insert new strings in the dictionary since * uncompressible data is probably not useful. This function is used * only for the level=0 compression option. * NOTE: this function should be optimized to avoid extra copying from * window to pending_buf. */ function deflate_stored(s, flush) { /* Stored blocks are limited to 0xffff bytes, pending_buf is limited * to pending_buf_size, and each stored block has a 5 byte header: */ var max_block_size = 0xffff; if (max_block_size > s.pending_buf_size - 5) { max_block_size = s.pending_buf_size - 5; } /* Copy as much as possible from input to output: */ for (;;) { /* Fill the window as much as possible: */ if (s.lookahead <= 1) { //Assert(s->strstart < s->w_size+MAX_DIST(s) || // s->block_start >= (long)s->w_size, "slide too late"); // if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || // s.block_start >= s.w_size)) { // throw new Error("slide too late"); // } fill_window(s); if (s.lookahead === 0 && flush === Z_NO_FLUSH) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ } //Assert(s->block_start >= 0L, "block gone"); // if (s.block_start < 0) throw new Error("block gone"); s.strstart += s.lookahead; s.lookahead = 0; /* Emit a stored block if pending_buf will be full: */ var max_start = s.block_start + max_block_size; if (s.strstart === 0 || s.strstart >= max_start) { /* strstart == 0 is possible when wraparound on 16-bit machine */ s.lookahead = s.strstart - max_start; s.strstart = max_start; /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } /* Flush if we may have to slide, otherwise block_start may become * negative and the data will be gone: */ if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = 0; if (flush === Z_FINISH) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.strstart > s.block_start) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_NEED_MORE; } /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ function deflate_fast(s, flush) { var hash_head; /* head of the hash chain */ var bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s.lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; /* flush the current block */ } } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = 0/*NIL*/; if (s.lookahead >= MIN_MATCH) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s.match_length = longest_match(s, hash_head); /* longest_match() sets match_start */ } if (s.match_length >= MIN_MATCH) { // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only /*** _tr_tally_dist(s, s.strstart - s.match_start, s.match_length - MIN_MATCH, bflush); ***/ bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); s.lookahead -= s.match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { s.match_length--; /* string at strstart already in table */ do { s.strstart++; /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s.match_length !== 0); s.strstart++; } else { s.strstart += s.match_length; s.match_length = 0; s.ins_h = s.window[s.strstart]; /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask; //#if MIN_MATCH != 3 // Call UPDATE_HASH() MIN_MATCH-3 more times //#endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ //Tracevv((stderr,"%c", s.window[s.strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = trees._tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; } if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1); if (flush === Z_FINISH) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.last_lit) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; } /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ function deflate_slow(s, flush) { var hash_head; /* head of hash chain */ var bflush; /* set if current block must be flushed */ var max_insert; /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s.lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = 0/*NIL*/; if (s.lookahead >= MIN_MATCH) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } /* Find the longest match, discarding those <= prev_length. */ s.prev_length = s.match_length; s.prev_match = s.match_start; s.match_length = MIN_MATCH - 1; if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s.match_length = longest_match(s, hash_head); /* longest_match() sets match_start */ if (s.match_length <= 5 && (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s.match_length = MIN_MATCH - 1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { max_insert = s.strstart + s.lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ //check_match(s, s.strstart-1, s.prev_match, s.prev_length); /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH, bflush);***/ bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s.lookahead -= s.prev_length - 1; s.prev_length -= 2; do { if (++s.strstart <= max_insert) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } } while (--s.prev_length !== 0); s.match_available = 0; s.match_length = MIN_MATCH - 1; s.strstart++; if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } else if (s.match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ //Tracevv((stderr,"%c", s->window[s->strstart-1])); /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]); if (bflush) { /*** FLUSH_BLOCK_ONLY(s, 0) ***/ flush_block_only(s, false); /***/ } s.strstart++; s.lookahead--; if (s.strm.avail_out === 0) { return BS_NEED_MORE; } } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s.match_available = 1; s.strstart++; s.lookahead--; } } //Assert (flush != Z_NO_FLUSH, "no flush?"); if (s.match_available) { //Tracevv((stderr,"%c", s->window[s->strstart-1])); /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]); s.match_available = 0; } s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; if (flush === Z_FINISH) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.last_lit) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; } /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ function deflate_rle(s, flush) { var bflush; /* set if current block must be flushed */ var prev; /* byte at distance one to match */ var scan, strend; /* scan goes up to strend for length of run */ var _win = s.window; for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the longest run, plus one for the unrolled loop. */ if (s.lookahead <= MAX_MATCH) { fill_window(s); if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ } /* See how many times the previous byte repeats */ s.match_length = 0; if (s.lookahead >= MIN_MATCH && s.strstart > 0) { scan = s.strstart - 1; prev = _win[scan]; if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { strend = s.strstart + MAX_MATCH; do { /*jshint noempty:false*/ } while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend); s.match_length = MAX_MATCH - (strend - scan); if (s.match_length > s.lookahead) { s.match_length = s.lookahead; } } //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ if (s.match_length >= MIN_MATCH) { //check_match(s, s.strstart, s.strstart - 1, s.match_length); /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH); s.lookahead -= s.match_length; s.strstart += s.match_length; s.match_length = 0; } else { /* No match, output a literal byte */ //Tracevv((stderr,"%c", s->window[s->strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = trees._tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; } if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = 0; if (flush === Z_FINISH) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.last_lit) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; } /* =========================================================================== * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ function deflate_huff(s, flush) { var bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we have a literal to write. */ if (s.lookahead === 0) { fill_window(s); if (s.lookahead === 0) { if (flush === Z_NO_FLUSH) { return BS_NEED_MORE; } break; /* flush the current block */ } } /* Output a literal byte */ s.match_length = 0; //Tracevv((stderr,"%c", s->window[s->strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ bflush = trees._tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; if (bflush) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } } s.insert = 0; if (flush === Z_FINISH) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { return BS_FINISH_STARTED; } /***/ return BS_FINISH_DONE; } if (s.last_lit) { /*** FLUSH_BLOCK(s, 0); ***/ flush_block_only(s, false); if (s.strm.avail_out === 0) { return BS_NEED_MORE; } /***/ } return BS_BLOCK_DONE; } /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ function Config(good_length, max_lazy, nice_length, max_chain, func) { this.good_length = good_length; this.max_lazy = max_lazy; this.nice_length = nice_length; this.max_chain = max_chain; this.func = func; } var configuration_table; configuration_table = [ /* good lazy nice chain */ new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ new Config(4, 5, 16, 8, deflate_fast), /* 2 */ new Config(4, 6, 32, 32, deflate_fast), /* 3 */ new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ new Config(8, 16, 32, 32, deflate_slow), /* 5 */ new Config(8, 16, 128, 128, deflate_slow), /* 6 */ new Config(8, 32, 128, 256, deflate_slow), /* 7 */ new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ ]; /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ function lm_init(s) { s.window_size = 2 * s.w_size; /*** CLEAR_HASH(s); ***/ zero(s.head); // Fill with NIL (= 0); /* Set the default configuration parameters: */ s.max_lazy_match = configuration_table[s.level].max_lazy; s.good_match = configuration_table[s.level].good_length; s.nice_match = configuration_table[s.level].nice_length; s.max_chain_length = configuration_table[s.level].max_chain; s.strstart = 0; s.block_start = 0; s.lookahead = 0; s.insert = 0; s.match_length = s.prev_length = MIN_MATCH - 1; s.match_available = 0; s.ins_h = 0; } function DeflateState() { this.strm = null; /* pointer back to this zlib stream */ this.status = 0; /* as the name implies */ this.pending_buf = null; /* output still pending */ this.pending_buf_size = 0; /* size of pending_buf */ this.pending_out = 0; /* next pending byte to output to the stream */ this.pending = 0; /* nb of bytes in the pending buffer */ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ this.gzhead = null; /* gzip header information to write */ this.gzindex = 0; /* where in extra, name, or comment */ this.method = Z_DEFLATED; /* can only be DEFLATED */ this.last_flush = -1; /* value of flush param for previous deflate call */ this.w_size = 0; /* LZ77 window size (32K by default) */ this.w_bits = 0; /* log2(w_size) (8..16) */ this.w_mask = 0; /* w_size - 1 */ this.window = null; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. */ this.window_size = 0; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ this.prev = null; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ this.head = null; /* Heads of the hash chains or NIL. */ this.ins_h = 0; /* hash index of string to be inserted */ this.hash_size = 0; /* number of elements in hash table */ this.hash_bits = 0; /* log2(hash_size) */ this.hash_mask = 0; /* hash_size-1 */ this.hash_shift = 0; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ this.block_start = 0; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ this.match_length = 0; /* length of best match */ this.prev_match = 0; /* previous match */ this.match_available = 0; /* set if previous match exists */ this.strstart = 0; /* start of string to insert */ this.match_start = 0; /* start of matching string */ this.lookahead = 0; /* number of valid bytes ahead in window */ this.prev_length = 0; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ this.max_chain_length = 0; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ this.max_lazy_match = 0; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ // That's alias to max_lazy_match, don't use directly //this.max_insert_length = 0; /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ this.level = 0; /* compression level (1..9) */ this.strategy = 0; /* favor or force Huffman coding*/ this.good_match = 0; /* Use a faster search when the previous match is longer than this */ this.nice_match = 0; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn't use ct_data typedef below to suppress compiler warning */ // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ // Use flat array of DOUBLE size, with interleaved fata, // because JS does not support effective this.dyn_ltree = new utils.Buf16(HEAP_SIZE * 2); this.dyn_dtree = new utils.Buf16((2 * D_CODES + 1) * 2); this.bl_tree = new utils.Buf16((2 * BL_CODES + 1) * 2); zero(this.dyn_ltree); zero(this.dyn_dtree); zero(this.bl_tree); this.l_desc = null; /* desc. for literal tree */ this.d_desc = null; /* desc. for distance tree */ this.bl_desc = null; /* desc. for bit length tree */ //ush bl_count[MAX_BITS+1]; this.bl_count = new utils.Buf16(MAX_BITS + 1); /* number of codes at each bit length for an optimal tree */ //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ this.heap = new utils.Buf16(2 * L_CODES + 1); /* heap used to build the Huffman trees */ zero(this.heap); this.heap_len = 0; /* number of elements in the heap */ this.heap_max = 0; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1]; zero(this.depth); /* Depth of each subtree used as tie breaker for trees of equal frequency */ this.l_buf = 0; /* buffer index for literals or lengths */ this.lit_bufsize = 0; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can't count above 4 */ this.last_lit = 0; /* running index in l_buf */ this.d_buf = 0; /* Buffer index for distances. To simplify the code, d_buf and l_buf have * the same number of elements. To use different lengths, an extra flag * array would be necessary. */ this.opt_len = 0; /* bit length of current block with optimal trees */ this.static_len = 0; /* bit length of current block with static trees */ this.matches = 0; /* number of string matches in current block */ this.insert = 0; /* bytes at end of window left to insert */ this.bi_buf = 0; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ this.bi_valid = 0; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ // Used for window memory init. We safely ignore it for JS. That makes // sense only for pointers and memory check tools. //this.high_water = 0; /* High water mark offset in window for initialized bytes -- bytes above * this are set to zero in order to avoid memory check warnings when * longest match routines access bytes past the input. This is then * updated to the new high water mark. */ } function deflateResetKeep(strm) { var s; if (!strm || !strm.state) { return err(strm, Z_STREAM_ERROR); } strm.total_in = strm.total_out = 0; strm.data_type = Z_UNKNOWN; s = strm.state; s.pending = 0; s.pending_out = 0; if (s.wrap < 0) { s.wrap = -s.wrap; /* was made negative by deflate(..., Z_FINISH); */ } s.status = (s.wrap ? INIT_STATE : BUSY_STATE); strm.adler = (s.wrap === 2) ? 0 // crc32(0, Z_NULL, 0) : 1; // adler32(0, Z_NULL, 0) s.last_flush = Z_NO_FLUSH; trees._tr_init(s); return Z_OK; } function deflateReset(strm) { var ret = deflateResetKeep(strm); if (ret === Z_OK) { lm_init(strm.state); } return ret; } function deflateSetHeader(strm, head) { if (!strm || !strm.state) { return Z_STREAM_ERROR; } if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; } strm.state.gzhead = head; return Z_OK; } function deflateInit2(strm, level, method, windowBits, memLevel, strategy) { if (!strm) { // === Z_NULL return Z_STREAM_ERROR; } var wrap = 1; if (level === Z_DEFAULT_COMPRESSION) { level = 6; } if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; windowBits = -windowBits; } else if (windowBits > 15) { wrap = 2; /* write gzip wrapper instead */ windowBits -= 16; } if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return err(strm, Z_STREAM_ERROR); } if (windowBits === 8) { windowBits = 9; } /* until 256-byte window bug fixed */ var s = new DeflateState(); strm.state = s; s.strm = strm; s.wrap = wrap; s.gzhead = null; s.w_bits = windowBits; s.w_size = 1 << s.w_bits; s.w_mask = s.w_size - 1; s.hash_bits = memLevel + 7; s.hash_size = 1 << s.hash_bits; s.hash_mask = s.hash_size - 1; s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); s.window = new utils.Buf8(s.w_size * 2); s.head = new utils.Buf16(s.hash_size); s.prev = new utils.Buf16(s.w_size); // Don't need mem init magic for JS. //s.high_water = 0; /* nothing written to s->window yet */ s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ s.pending_buf_size = s.lit_bufsize * 4; //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); //s->pending_buf = (uchf *) overlay; s.pending_buf = new utils.Buf8(s.pending_buf_size); // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) //s->d_buf = overlay + s->lit_bufsize/sizeof(ush); s.d_buf = 1 * s.lit_bufsize; //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; s.l_buf = (1 + 2) * s.lit_bufsize; s.level = level; s.strategy = strategy; s.method = method; return deflateReset(strm); } function deflateInit(strm, level) { return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); } function deflate(strm, flush) { var old_flush, s; var beg, val; // for gzip header write only if (!strm || !strm.state || flush > Z_BLOCK || flush < 0) { return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR; } s = strm.state; if (!strm.output || (!strm.input && strm.avail_in !== 0) || (s.status === FINISH_STATE && flush !== Z_FINISH)) { return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR); } s.strm = strm; /* just in case */ old_flush = s.last_flush; s.last_flush = flush; /* Write the header */ if (s.status === INIT_STATE) { if (s.wrap === 2) { // GZIP header strm.adler = 0; //crc32(0L, Z_NULL, 0); put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); if (!s.gzhead) { // s->gzhead == Z_NULL put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, s.level === 9 ? 2 : (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0)); put_byte(s, OS_CODE); s.status = BUSY_STATE; } else { put_byte(s, (s.gzhead.text ? 1 : 0) + (s.gzhead.hcrc ? 2 : 0) + (!s.gzhead.extra ? 0 : 4) + (!s.gzhead.name ? 0 : 8) + (!s.gzhead.comment ? 0 : 16) ); put_byte(s, s.gzhead.time & 0xff); put_byte(s, (s.gzhead.time >> 8) & 0xff); put_byte(s, (s.gzhead.time >> 16) & 0xff); put_byte(s, (s.gzhead.time >> 24) & 0xff); put_byte(s, s.level === 9 ? 2 : (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0)); put_byte(s, s.gzhead.os & 0xff); if (s.gzhead.extra && s.gzhead.extra.length) { put_byte(s, s.gzhead.extra.length & 0xff); put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); } if (s.gzhead.hcrc) { strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0); } s.gzindex = 0; s.status = EXTRA_STATE; } } else // DEFLATE header { var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; var level_flags = -1; if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { level_flags = 0; } else if (s.level < 6) { level_flags = 1; } else if (s.level === 6) { level_flags = 2; } else { level_flags = 3; } header |= (level_flags << 6); if (s.strstart !== 0) { header |= PRESET_DICT; } header += 31 - (header % 31); s.status = BUSY_STATE; putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s.strstart !== 0) { putShortMSB(s, strm.adler >>> 16); putShortMSB(s, strm.adler & 0xffff); } strm.adler = 1; // adler32(0L, Z_NULL, 0); } } //#ifdef GZIP if (s.status === EXTRA_STATE) { if (s.gzhead.extra/* != Z_NULL*/) { beg = s.pending; /* start of bytes to update crc */ while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { if (s.pending === s.pending_buf_size) { if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); } flush_pending(strm); beg = s.pending; if (s.pending === s.pending_buf_size) { break; } } put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); s.gzindex++; } if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); } if (s.gzindex === s.gzhead.extra.length) { s.gzindex = 0; s.status = NAME_STATE; } } else { s.status = NAME_STATE; } } if (s.status === NAME_STATE) { if (s.gzhead.name/* != Z_NULL*/) { beg = s.pending; /* start of bytes to update crc */ //int val; do { if (s.pending === s.pending_buf_size) { if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); } flush_pending(strm); beg = s.pending; if (s.pending === s.pending_buf_size) { val = 1; break; } } // JS specific: little magic to add zero terminator to end of string if (s.gzindex < s.gzhead.name.length) { val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; } else { val = 0; } put_byte(s, val); } while (val !== 0); if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); } if (val === 0) { s.gzindex = 0; s.status = COMMENT_STATE; } } else { s.status = COMMENT_STATE; } } if (s.status === COMMENT_STATE) { if (s.gzhead.comment/* != Z_NULL*/) { beg = s.pending; /* start of bytes to update crc */ //int val; do { if (s.pending === s.pending_buf_size) { if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); } flush_pending(strm); beg = s.pending; if (s.pending === s.pending_buf_size) { val = 1; break; } } // JS specific: little magic to add zero terminator to end of string if (s.gzindex < s.gzhead.comment.length) { val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; } else { val = 0; } put_byte(s, val); } while (val !== 0); if (s.gzhead.hcrc && s.pending > beg) { strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); } if (val === 0) { s.status = HCRC_STATE; } } else { s.status = HCRC_STATE; } } if (s.status === HCRC_STATE) { if (s.gzhead.hcrc) { if (s.pending + 2 > s.pending_buf_size) { flush_pending(strm); } if (s.pending + 2 <= s.pending_buf_size) { put_byte(s, strm.adler & 0xff); put_byte(s, (strm.adler >> 8) & 0xff); strm.adler = 0; //crc32(0L, Z_NULL, 0); s.status = BUSY_STATE; } } else { s.status = BUSY_STATE; } } //#endif /* Flush as much pending output as possible */ if (s.pending !== 0) { flush_pending(strm); if (strm.avail_out === 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won't be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s.last_flush = -1; return Z_OK; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && flush !== Z_FINISH) { return err(strm, Z_BUF_ERROR); } /* User must not provide more input after the first FINISH: */ if (s.status === FINISH_STATE && strm.avail_in !== 0) { return err(strm, Z_BUF_ERROR); } /* Start a new block or continue the current one. */ if (strm.avail_in !== 0 || s.lookahead !== 0 || (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) { var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : (s.strategy === Z_RLE ? deflate_rle(s, flush) : configuration_table[s.level].func(s, flush)); if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { s.status = FINISH_STATE; } if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { if (strm.avail_out === 0) { s.last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate === BS_BLOCK_DONE) { if (flush === Z_PARTIAL_FLUSH) { trees._tr_align(s); } else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ trees._tr_stored_block(s, 0, 0, false); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush === Z_FULL_FLUSH) { /*** CLEAR_HASH(s); ***/ /* forget history */ zero(s.head); // Fill with NIL (= 0); if (s.lookahead === 0) { s.strstart = 0; s.block_start = 0; s.insert = 0; } } } flush_pending(strm); if (strm.avail_out === 0) { s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK; } } } //Assert(strm->avail_out > 0, "bug2"); //if (strm.avail_out <= 0) { throw new Error("bug2");} if (flush !== Z_FINISH) { return Z_OK; } if (s.wrap <= 0) { return Z_STREAM_END; } /* Write the trailer */ if (s.wrap === 2) { put_byte(s, strm.adler & 0xff); put_byte(s, (strm.adler >> 8) & 0xff); put_byte(s, (strm.adler >> 16) & 0xff); put_byte(s, (strm.adler >> 24) & 0xff); put_byte(s, strm.total_in & 0xff); put_byte(s, (strm.total_in >> 8) & 0xff); put_byte(s, (strm.total_in >> 16) & 0xff); put_byte(s, (strm.total_in >> 24) & 0xff); } else { putShortMSB(s, strm.adler >>> 16); putShortMSB(s, strm.adler & 0xffff); } flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ if (s.wrap > 0) { s.wrap = -s.wrap; } /* write the trailer only once! */ return s.pending !== 0 ? Z_OK : Z_STREAM_END; } function deflateEnd(strm) { var status; if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { return Z_STREAM_ERROR; } status = strm.state.status; if (status !== INIT_STATE && status !== EXTRA_STATE && status !== NAME_STATE && status !== COMMENT_STATE && status !== HCRC_STATE && status !== BUSY_STATE && status !== FINISH_STATE ) { return err(strm, Z_STREAM_ERROR); } strm.state = null; return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK; } /* ========================================================================= * Initializes the compression dictionary from the given byte * sequence without producing any compressed output. */ function deflateSetDictionary(strm, dictionary) { var dictLength = dictionary.length; var s; var str, n; var wrap; var avail; var next; var input; var tmpDict; if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { return Z_STREAM_ERROR; } s = strm.state; wrap = s.wrap; if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { return Z_STREAM_ERROR; } /* when using zlib wrappers, compute Adler-32 for provided dictionary */ if (wrap === 1) { /* adler32(strm->adler, dictionary, dictLength); */ strm.adler = adler32(strm.adler, dictionary, dictLength, 0); } s.wrap = 0; /* avoid computing Adler-32 in read_buf */ /* if dictionary would fill window, just replace the history */ if (dictLength >= s.w_size) { if (wrap === 0) { /* already empty otherwise */ /*** CLEAR_HASH(s); ***/ zero(s.head); // Fill with NIL (= 0); s.strstart = 0; s.block_start = 0; s.insert = 0; } /* use the tail */ // dictionary = dictionary.slice(dictLength - s.w_size); tmpDict = new utils.Buf8(s.w_size); utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0); dictionary = tmpDict; dictLength = s.w_size; } /* insert dictionary into window and hash */ avail = strm.avail_in; next = strm.next_in; input = strm.input; strm.avail_in = dictLength; strm.next_in = 0; strm.input = dictionary; fill_window(s); while (s.lookahead >= MIN_MATCH) { str = s.strstart; n = s.lookahead - (MIN_MATCH - 1); do { /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; s.prev[str & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = str; str++; } while (--n); s.strstart = str; s.lookahead = MIN_MATCH - 1; fill_window(s); } s.strstart += s.lookahead; s.block_start = s.strstart; s.insert = s.lookahead; s.lookahead = 0; s.match_length = s.prev_length = MIN_MATCH - 1; s.match_available = 0; strm.next_in = next; strm.input = input; strm.avail_in = avail; s.wrap = wrap; return Z_OK; } exports.deflateInit = deflateInit; exports.deflateInit2 = deflateInit2; exports.deflateReset = deflateReset; exports.deflateResetKeep = deflateResetKeep; exports.deflateSetHeader = deflateSetHeader; exports.deflate = deflate; exports.deflateEnd = deflateEnd; exports.deflateSetDictionary = deflateSetDictionary; exports.deflateInfo = 'pako deflate (from Nodeca project)'; /* Not implemented exports.deflateBound = deflateBound; exports.deflateCopy = deflateCopy; exports.deflateParams = deflateParams; exports.deflatePending = deflatePending; exports.deflatePrime = deflatePrime; exports.deflateTune = deflateTune; */ },{"../utils/common":188,"./adler32":189,"./crc32":191,"./messages":196,"./trees":197}],193:[function(require,module,exports){ 'use strict'; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // See state defs from inflate.js var BAD = 30; /* got a data error -- remain here until reset */ var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state.mode === LEN strm.avail_in >= 6 strm.avail_out >= 258 start >= strm.avail_out state.bits < 8 On return, state.mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm.avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm.avail_out >= 258 for each loop to avoid checking for output space. */ module.exports = function inflate_fast(strm, start) { var state; var _in; /* local strm.input */ var last; /* have enough input while in < last */ var _out; /* local strm.output */ var beg; /* inflate()'s initial strm.output */ var end; /* while out < end, enough space available */ //#ifdef INFLATE_STRICT var dmax; /* maximum distance from zlib header */ //#endif var wsize; /* window size or zero if not using window */ var whave; /* valid bytes in the window */ var wnext; /* window write index */ // Use `s_window` instead `window`, avoid conflict with instrumentation tools var s_window; /* allocated sliding window, if wsize != 0 */ var hold; /* local strm.hold */ var bits; /* local strm.bits */ var lcode; /* local strm.lencode */ var dcode; /* local strm.distcode */ var lmask; /* mask for first level of length codes */ var dmask; /* mask for first level of distance codes */ var here; /* retrieved table entry */ var op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ var len; /* match length, unused bytes */ var dist; /* match distance */ var from; /* where to copy match from */ var from_source; var input, output; // JS specific, because we have no pointers /* copy state to local variables */ state = strm.state; //here = state.here; _in = strm.next_in; input = strm.input; last = _in + (strm.avail_in - 5); _out = strm.next_out; output = strm.output; beg = _out - (start - strm.avail_out); end = _out + (strm.avail_out - 257); //#ifdef INFLATE_STRICT dmax = state.dmax; //#endif wsize = state.wsize; whave = state.whave; wnext = state.wnext; s_window = state.window; hold = state.hold; bits = state.bits; lcode = state.lencode; dcode = state.distcode; lmask = (1 << state.lenbits) - 1; dmask = (1 << state.distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ top: do { if (bits < 15) { hold += input[_in++] << bits; bits += 8; hold += input[_in++] << bits; bits += 8; } here = lcode[hold & lmask]; dolen: for (;;) { // Goto emulation op = here >>> 24/*here.bits*/; hold >>>= op; bits -= op; op = (here >>> 16) & 0xff/*here.op*/; if (op === 0) { /* literal */ //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? // "inflate: literal '%c'\n" : // "inflate: literal 0x%02x\n", here.val)); output[_out++] = here & 0xffff/*here.val*/; } else if (op & 16) { /* length base */ len = here & 0xffff/*here.val*/; op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += input[_in++] << bits; bits += 8; } len += hold & ((1 << op) - 1); hold >>>= op; bits -= op; } //Tracevv((stderr, "inflate: length %u\n", len)); if (bits < 15) { hold += input[_in++] << bits; bits += 8; hold += input[_in++] << bits; bits += 8; } here = dcode[hold & dmask]; dodist: for (;;) { // goto emulation op = here >>> 24/*here.bits*/; hold >>>= op; bits -= op; op = (here >>> 16) & 0xff/*here.op*/; if (op & 16) { /* distance base */ dist = here & 0xffff/*here.val*/; op &= 15; /* number of extra bits */ if (bits < op) { hold += input[_in++] << bits; bits += 8; if (bits < op) { hold += input[_in++] << bits; bits += 8; } } dist += hold & ((1 << op) - 1); //#ifdef INFLATE_STRICT if (dist > dmax) { strm.msg = 'invalid distance too far back'; state.mode = BAD; break top; } //#endif hold >>>= op; bits -= op; //Tracevv((stderr, "inflate: distance %u\n", dist)); op = _out - beg; /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { if (state.sane) { strm.msg = 'invalid distance too far back'; state.mode = BAD; break top; } // (!) This block is disabled in zlib defaults, // don't enable it for binary compatibility //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR // if (len <= op - whave) { // do { // output[_out++] = 0; // } while (--len); // continue top; // } // len -= op - whave; // do { // output[_out++] = 0; // } while (--op > whave); // if (op === 0) { // from = _out - dist; // do { // output[_out++] = output[from++]; // } while (--len); // continue top; // } //#endif } from = 0; // window index from_source = s_window; if (wnext === 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } else if (wnext < op) { /* wrap around window */ from += wsize + wnext - op; op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = 0; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } } else { /* contiguous in window */ from += wnext - op; if (op < len) { /* some from window */ len -= op; do { output[_out++] = s_window[from++]; } while (--op); from = _out - dist; /* rest from output */ from_source = output; } } while (len > 2) { output[_out++] = from_source[from++]; output[_out++] = from_source[from++]; output[_out++] = from_source[from++]; len -= 3; } if (len) { output[_out++] = from_source[from++]; if (len > 1) { output[_out++] = from_source[from++]; } } } else { from = _out - dist; /* copy direct from output */ do { /* minimum length is three */ output[_out++] = output[from++]; output[_out++] = output[from++]; output[_out++] = output[from++]; len -= 3; } while (len > 2); if (len) { output[_out++] = output[from++]; if (len > 1) { output[_out++] = output[from++]; } } } } else if ((op & 64) === 0) { /* 2nd level distance code */ here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; continue dodist; } else { strm.msg = 'invalid distance code'; state.mode = BAD; break top; } break; // need to emulate goto via "continue" } } else if ((op & 64) === 0) { /* 2nd level length code */ here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; continue dolen; } else if (op & 32) { /* end-of-block */ //Tracevv((stderr, "inflate: end of block\n")); state.mode = TYPE; break top; } else { strm.msg = 'invalid literal/length code'; state.mode = BAD; break top; } break; // need to emulate goto via "continue" } } while (_in < last && _out < end); /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ len = bits >> 3; _in -= len; bits -= len << 3; hold &= (1 << bits) - 1; /* update state and return */ strm.next_in = _in; strm.next_out = _out; strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); state.hold = hold; state.bits = bits; return; }; },{}],194:[function(require,module,exports){ 'use strict'; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. var utils = require('../utils/common'); var adler32 = require('./adler32'); var crc32 = require('./crc32'); var inflate_fast = require('./inffast'); var inflate_table = require('./inftrees'); var CODES = 0; var LENS = 1; var DISTS = 2; /* Public constants ==========================================================*/ /* ===========================================================================*/ /* Allowed flush values; see deflate() and inflate() below for details */ //var Z_NO_FLUSH = 0; //var Z_PARTIAL_FLUSH = 1; //var Z_SYNC_FLUSH = 2; //var Z_FULL_FLUSH = 3; var Z_FINISH = 4; var Z_BLOCK = 5; var Z_TREES = 6; /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ var Z_OK = 0; var Z_STREAM_END = 1; var Z_NEED_DICT = 2; //var Z_ERRNO = -1; var Z_STREAM_ERROR = -2; var Z_DATA_ERROR = -3; var Z_MEM_ERROR = -4; var Z_BUF_ERROR = -5; //var Z_VERSION_ERROR = -6; /* The deflate compression method */ var Z_DEFLATED = 8; /* STATES ====================================================================*/ /* ===========================================================================*/ var HEAD = 1; /* i: waiting for magic header */ var FLAGS = 2; /* i: waiting for method and flags (gzip) */ var TIME = 3; /* i: waiting for modification time (gzip) */ var OS = 4; /* i: waiting for extra flags and operating system (gzip) */ var EXLEN = 5; /* i: waiting for extra length (gzip) */ var EXTRA = 6; /* i: waiting for extra bytes (gzip) */ var NAME = 7; /* i: waiting for end of file name (gzip) */ var COMMENT = 8; /* i: waiting for end of comment (gzip) */ var HCRC = 9; /* i: waiting for header crc (gzip) */ var DICTID = 10; /* i: waiting for dictionary check value */ var DICT = 11; /* waiting for inflateSetDictionary() call */ var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ var STORED = 14; /* i: waiting for stored size (length and complement) */ var COPY_ = 15; /* i/o: same as COPY below, but only first time in */ var COPY = 16; /* i/o: waiting for input or output to copy stored block */ var TABLE = 17; /* i: waiting for dynamic block table lengths */ var LENLENS = 18; /* i: waiting for code length code lengths */ var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ var LEN_ = 20; /* i: same as LEN below, but only first time in */ var LEN = 21; /* i: waiting for length/lit/eob code */ var LENEXT = 22; /* i: waiting for length extra bits */ var DIST = 23; /* i: waiting for distance code */ var DISTEXT = 24; /* i: waiting for distance extra bits */ var MATCH = 25; /* o: waiting for output space to copy string */ var LIT = 26; /* o: waiting for output space to write literal */ var CHECK = 27; /* i: waiting for 32-bit check value */ var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ var DONE = 29; /* finished check, done -- remain here until reset */ var BAD = 30; /* got a data error -- remain here until reset */ var MEM = 31; /* got an inflate() memory error -- remain here until reset */ var SYNC = 32; /* looking for synchronization bytes to restart inflate() */ /* ===========================================================================*/ var ENOUGH_LENS = 852; var ENOUGH_DISTS = 592; //var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); var MAX_WBITS = 15; /* 32K LZ77 window */ var DEF_WBITS = MAX_WBITS; function zswap32(q) { return (((q >>> 24) & 0xff) + ((q >>> 8) & 0xff00) + ((q & 0xff00) << 8) + ((q & 0xff) << 24)); } function InflateState() { this.mode = 0; /* current inflate mode */ this.last = false; /* true if processing last block */ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ this.havedict = false; /* true if dictionary provided */ this.flags = 0; /* gzip header method and flags (0 if zlib) */ this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ this.check = 0; /* protected copy of check value */ this.total = 0; /* protected copy of output count */ // TODO: may be {} this.head = null; /* where to save gzip header information */ /* sliding window */ this.wbits = 0; /* log base 2 of requested window size */ this.wsize = 0; /* window size or zero if not using window */ this.whave = 0; /* valid bytes in the window */ this.wnext = 0; /* window write index */ this.window = null; /* allocated sliding window, if needed */ /* bit accumulator */ this.hold = 0; /* input bit accumulator */ this.bits = 0; /* number of bits in "in" */ /* for string and stored block copying */ this.length = 0; /* literal or length of data to copy */ this.offset = 0; /* distance back to copy string from */ /* for table and code decoding */ this.extra = 0; /* extra bits needed */ /* fixed and dynamic code tables */ this.lencode = null; /* starting table for length/literal codes */ this.distcode = null; /* starting table for distance codes */ this.lenbits = 0; /* index bits for lencode */ this.distbits = 0; /* index bits for distcode */ /* dynamic table building */ this.ncode = 0; /* number of code length code lengths */ this.nlen = 0; /* number of length code lengths */ this.ndist = 0; /* number of distance code lengths */ this.have = 0; /* number of code lengths in lens[] */ this.next = null; /* next available space in codes[] */ this.lens = new utils.Buf16(320); /* temporary storage for code lengths */ this.work = new utils.Buf16(288); /* work area for code table building */ /* because we don't have pointers in js, we use lencode and distcode directly as buffers so we don't need codes */ //this.codes = new utils.Buf32(ENOUGH); /* space for code tables */ this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ this.distdyn = null; /* dynamic table for distance codes (JS specific) */ this.sane = 0; /* if false, allow invalid distance too far */ this.back = 0; /* bits back of last unprocessed length/lit */ this.was = 0; /* initial length of match */ } function inflateResetKeep(strm) { var state; if (!strm || !strm.state) { return Z_STREAM_ERROR; } state = strm.state; strm.total_in = strm.total_out = state.total = 0; strm.msg = ''; /*Z_NULL*/ if (state.wrap) { /* to support ill-conceived Java test suite */ strm.adler = state.wrap & 1; } state.mode = HEAD; state.last = 0; state.havedict = 0; state.dmax = 32768; state.head = null/*Z_NULL*/; state.hold = 0; state.bits = 0; //state.lencode = state.distcode = state.next = state.codes; state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS); state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS); state.sane = 1; state.back = -1; //Tracev((stderr, "inflate: reset\n")); return Z_OK; } function inflateReset(strm) { var state; if (!strm || !strm.state) { return Z_STREAM_ERROR; } state = strm.state; state.wsize = 0; state.whave = 0; state.wnext = 0; return inflateResetKeep(strm); } function inflateReset2(strm, windowBits) { var wrap; var state; /* get the state */ if (!strm || !strm.state) { return Z_STREAM_ERROR; } state = strm.state; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { wrap = 0; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 1; if (windowBits < 48) { windowBits &= 15; } } /* set number of window bits, free window if different */ if (windowBits && (windowBits < 8 || windowBits > 15)) { return Z_STREAM_ERROR; } if (state.window !== null && state.wbits !== windowBits) { state.window = null; } /* update state and reset the rest of it */ state.wrap = wrap; state.wbits = windowBits; return inflateReset(strm); } function inflateInit2(strm, windowBits) { var ret; var state; if (!strm) { return Z_STREAM_ERROR; } //strm.msg = Z_NULL; /* in case we return an error */ state = new InflateState(); //if (state === Z_NULL) return Z_MEM_ERROR; //Tracev((stderr, "inflate: allocated\n")); strm.state = state; state.window = null/*Z_NULL*/; ret = inflateReset2(strm, windowBits); if (ret !== Z_OK) { strm.state = null/*Z_NULL*/; } return ret; } function inflateInit(strm) { return inflateInit2(strm, DEF_WBITS); } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ var virgin = true; var lenfix, distfix; // We have no pointers in JS, so keep tables separate function fixedtables(state) { /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { var sym; lenfix = new utils.Buf32(512); distfix = new utils.Buf32(32); /* literal/length table */ sym = 0; while (sym < 144) { state.lens[sym++] = 8; } while (sym < 256) { state.lens[sym++] = 9; } while (sym < 280) { state.lens[sym++] = 7; } while (sym < 288) { state.lens[sym++] = 8; } inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); /* distance table */ sym = 0; while (sym < 32) { state.lens[sym++] = 5; } inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); /* do this just once */ virgin = false; } state.lencode = lenfix; state.lenbits = 9; state.distcode = distfix; state.distbits = 5; } /* Update the window with the last wsize (normally 32K) bytes written before returning. If window does not exist yet, create it. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to create a window for dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ function updatewindow(strm, src, end, copy) { var dist; var state = strm.state; /* if it hasn't been done already, allocate space for the window */ if (state.window === null) { state.wsize = 1 << state.wbits; state.wnext = 0; state.whave = 0; state.window = new utils.Buf8(state.wsize); } /* copy state->wsize or less output bytes into the circular window */ if (copy >= state.wsize) { utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0); state.wnext = 0; state.whave = state.wsize; } else { dist = state.wsize - state.wnext; if (dist > copy) { dist = copy; } //zmemcpy(state->window + state->wnext, end - copy, dist); utils.arraySet(state.window, src, end - copy, dist, state.wnext); copy -= dist; if (copy) { //zmemcpy(state->window, end - copy, copy); utils.arraySet(state.window, src, end - copy, copy, 0); state.wnext = copy; state.whave = state.wsize; } else { state.wnext += dist; if (state.wnext === state.wsize) { state.wnext = 0; } if (state.whave < state.wsize) { state.whave += dist; } } } return 0; } function inflate(strm, flush) { var state; var input, output; // input/output buffers var next; /* next input INDEX */ var put; /* next output INDEX */ var have, left; /* available input and output */ var hold; /* bit buffer */ var bits; /* bits in bit buffer */ var _in, _out; /* save starting available input and output */ var copy; /* number of stored or match bytes to copy */ var from; /* where to copy match bytes from */ var from_source; var here = 0; /* current decoding table entry */ var here_bits, here_op, here_val; // paked "here" denormalized (JS specific) //var last; /* parent table entry */ var last_bits, last_op, last_val; // paked "last" denormalized (JS specific) var len; /* length to copy for repeats, bits to drop */ var ret; /* return code */ var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */ var opts; var n; // temporary var for NEED_BITS var order = /* permutation of code lengths */ [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; if (!strm || !strm.state || !strm.output || (!strm.input && strm.avail_in !== 0)) { return Z_STREAM_ERROR; } state = strm.state; if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ //--- LOAD() --- put = strm.next_out; output = strm.output; left = strm.avail_out; next = strm.next_in; input = strm.input; have = strm.avail_in; hold = state.hold; bits = state.bits; //--- _in = have; _out = left; ret = Z_OK; inf_leave: // goto emulation for (;;) { switch (state.mode) { case HEAD: if (state.wrap === 0) { state.mode = TYPEDO; break; } //=== NEEDBITS(16); while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ state.check = 0/*crc32(0L, Z_NULL, 0)*/; //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32(state.check, hbuf, 2, 0); //===// //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = FLAGS; break; } state.flags = 0; /* expect zlib header */ if (state.head) { state.head.done = false; } if (!(state.wrap & 1) || /* check if zlib header allowed */ (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { strm.msg = 'incorrect header check'; state.mode = BAD; break; } if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { strm.msg = 'unknown compression method'; state.mode = BAD; break; } //--- DROPBITS(4) ---// hold >>>= 4; bits -= 4; //---// len = (hold & 0x0f)/*BITS(4)*/ + 8; if (state.wbits === 0) { state.wbits = len; } else if (len > state.wbits) { strm.msg = 'invalid window size'; state.mode = BAD; break; } state.dmax = 1 << len; //Tracev((stderr, "inflate: zlib header ok\n")); strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; state.mode = hold & 0x200 ? DICTID : TYPE; //=== INITBITS(); hold = 0; bits = 0; //===// break; case FLAGS: //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.flags = hold; if ((state.flags & 0xff) !== Z_DEFLATED) { strm.msg = 'unknown compression method'; state.mode = BAD; break; } if (state.flags & 0xe000) { strm.msg = 'unknown header flags set'; state.mode = BAD; break; } if (state.head) { state.head.text = ((hold >> 8) & 1); } if (state.flags & 0x0200) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = TIME; /* falls through */ case TIME: //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (state.head) { state.head.time = hold; } if (state.flags & 0x0200) { //=== CRC4(state.check, hold) hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; hbuf[2] = (hold >>> 16) & 0xff; hbuf[3] = (hold >>> 24) & 0xff; state.check = crc32(state.check, hbuf, 4, 0); //=== } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = OS; /* falls through */ case OS: //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (state.head) { state.head.xflags = (hold & 0xff); state.head.os = (hold >> 8); } if (state.flags & 0x0200) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = EXLEN; /* falls through */ case EXLEN: if (state.flags & 0x0400) { //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.length = hold; if (state.head) { state.head.extra_len = hold; } if (state.flags & 0x0200) { //=== CRC2(state.check, hold); hbuf[0] = hold & 0xff; hbuf[1] = (hold >>> 8) & 0xff; state.check = crc32(state.check, hbuf, 2, 0); //===// } //=== INITBITS(); hold = 0; bits = 0; //===// } else if (state.head) { state.head.extra = null/*Z_NULL*/; } state.mode = EXTRA; /* falls through */ case EXTRA: if (state.flags & 0x0400) { copy = state.length; if (copy > have) { copy = have; } if (copy) { if (state.head) { len = state.head.extra_len - state.length; if (!state.head.extra) { // Use untyped array for more convenient processing later state.head.extra = new Array(state.head.extra_len); } utils.arraySet( state.head.extra, input, next, // extra field is limited to 65536 bytes // - no need for additional size check copy, /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ len ); //zmemcpy(state.head.extra + len, next, // len + copy > state.head.extra_max ? // state.head.extra_max - len : copy); } if (state.flags & 0x0200) { state.check = crc32(state.check, input, copy, next); } have -= copy; next += copy; state.length -= copy; } if (state.length) { break inf_leave; } } state.length = 0; state.mode = NAME; /* falls through */ case NAME: if (state.flags & 0x0800) { if (have === 0) { break inf_leave; } copy = 0; do { // TODO: 2 or 1 bytes? len = input[next + copy++]; /* use constant limit because in js we should not preallocate memory */ if (state.head && len && (state.length < 65536 /*state.head.name_max*/)) { state.head.name += String.fromCharCode(len); } } while (len && copy < have); if (state.flags & 0x0200) { state.check = crc32(state.check, input, copy, next); } have -= copy; next += copy; if (len) { break inf_leave; } } else if (state.head) { state.head.name = null; } state.length = 0; state.mode = COMMENT; /* falls through */ case COMMENT: if (state.flags & 0x1000) { if (have === 0) { break inf_leave; } copy = 0; do { len = input[next + copy++]; /* use constant limit because in js we should not preallocate memory */ if (state.head && len && (state.length < 65536 /*state.head.comm_max*/)) { state.head.comment += String.fromCharCode(len); } } while (len && copy < have); if (state.flags & 0x0200) { state.check = crc32(state.check, input, copy, next); } have -= copy; next += copy; if (len) { break inf_leave; } } else if (state.head) { state.head.comment = null; } state.mode = HCRC; /* falls through */ case HCRC: if (state.flags & 0x0200) { //=== NEEDBITS(16); */ while (bits < 16) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (hold !== (state.check & 0xffff)) { strm.msg = 'header crc mismatch'; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// } if (state.head) { state.head.hcrc = ((state.flags >> 9) & 1); state.head.done = true; } strm.adler = state.check = 0; state.mode = TYPE; break; case DICTID: //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// strm.adler = state.check = zswap32(hold); //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = DICT; /* falls through */ case DICT: if (state.havedict === 0) { //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- return Z_NEED_DICT; } strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; state.mode = TYPE; /* falls through */ case TYPE: if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } /* falls through */ case TYPEDO: if (state.last) { //--- BYTEBITS() ---// hold >>>= bits & 7; bits -= bits & 7; //---// state.mode = CHECK; break; } //=== NEEDBITS(3); */ while (bits < 3) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.last = (hold & 0x01)/*BITS(1)*/; //--- DROPBITS(1) ---// hold >>>= 1; bits -= 1; //---// switch ((hold & 0x03)/*BITS(2)*/) { case 0: /* stored block */ //Tracev((stderr, "inflate: stored block%s\n", // state.last ? " (last)" : "")); state.mode = STORED; break; case 1: /* fixed block */ fixedtables(state); //Tracev((stderr, "inflate: fixed codes block%s\n", // state.last ? " (last)" : "")); state.mode = LEN_; /* decode codes */ if (flush === Z_TREES) { //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// break inf_leave; } break; case 2: /* dynamic block */ //Tracev((stderr, "inflate: dynamic codes block%s\n", // state.last ? " (last)" : "")); state.mode = TABLE; break; case 3: strm.msg = 'invalid block type'; state.mode = BAD; } //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// break; case STORED: //--- BYTEBITS() ---// /* go to byte boundary */ hold >>>= bits & 7; bits -= bits & 7; //---// //=== NEEDBITS(32); */ while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { strm.msg = 'invalid stored block lengths'; state.mode = BAD; break; } state.length = hold & 0xffff; //Tracev((stderr, "inflate: stored length %u\n", // state.length)); //=== INITBITS(); hold = 0; bits = 0; //===// state.mode = COPY_; if (flush === Z_TREES) { break inf_leave; } /* falls through */ case COPY_: state.mode = COPY; /* falls through */ case COPY: copy = state.length; if (copy) { if (copy > have) { copy = have; } if (copy > left) { copy = left; } if (copy === 0) { break inf_leave; } //--- zmemcpy(put, next, copy); --- utils.arraySet(output, input, next, copy, put); //---// have -= copy; next += copy; left -= copy; put += copy; state.length -= copy; break; } //Tracev((stderr, "inflate: stored end\n")); state.mode = TYPE; break; case TABLE: //=== NEEDBITS(14); */ while (bits < 14) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; //--- DROPBITS(5) ---// hold >>>= 5; bits -= 5; //---// state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; //--- DROPBITS(5) ---// hold >>>= 5; bits -= 5; //---// state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; //--- DROPBITS(4) ---// hold >>>= 4; bits -= 4; //---// //#ifndef PKZIP_BUG_WORKAROUND if (state.nlen > 286 || state.ndist > 30) { strm.msg = 'too many length or distance symbols'; state.mode = BAD; break; } //#endif //Tracev((stderr, "inflate: table sizes ok\n")); state.have = 0; state.mode = LENLENS; /* falls through */ case LENLENS: while (state.have < state.ncode) { //=== NEEDBITS(3); while (bits < 3) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); //--- DROPBITS(3) ---// hold >>>= 3; bits -= 3; //---// } while (state.have < 19) { state.lens[order[state.have++]] = 0; } // We have separate tables & no pointers. 2 commented lines below not needed. //state.next = state.codes; //state.lencode = state.next; // Switch to use dynamic table state.lencode = state.lendyn; state.lenbits = 7; opts = { bits: state.lenbits }; ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); state.lenbits = opts.bits; if (ret) { strm.msg = 'invalid code lengths set'; state.mode = BAD; break; } //Tracev((stderr, "inflate: code lengths ok\n")); state.have = 0; state.mode = CODELENS; /* falls through */ case CODELENS: while (state.have < state.nlen + state.ndist) { for (;;) { here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if (here_val < 16) { //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.lens[state.have++] = here_val; } else { if (here_val === 16) { //=== NEEDBITS(here.bits + 2); n = here_bits + 2; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// if (state.have === 0) { strm.msg = 'invalid bit length repeat'; state.mode = BAD; break; } len = state.lens[state.have - 1]; copy = 3 + (hold & 0x03);//BITS(2); //--- DROPBITS(2) ---// hold >>>= 2; bits -= 2; //---// } else if (here_val === 17) { //=== NEEDBITS(here.bits + 3); n = here_bits + 3; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// len = 0; copy = 3 + (hold & 0x07);//BITS(3); //--- DROPBITS(3) ---// hold >>>= 3; bits -= 3; //---// } else { //=== NEEDBITS(here.bits + 7); n = here_bits + 7; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// len = 0; copy = 11 + (hold & 0x7f);//BITS(7); //--- DROPBITS(7) ---// hold >>>= 7; bits -= 7; //---// } if (state.have + copy > state.nlen + state.ndist) { strm.msg = 'invalid bit length repeat'; state.mode = BAD; break; } while (copy--) { state.lens[state.have++] = len; } } } /* handle error breaks in while */ if (state.mode === BAD) { break; } /* check for end-of-block code (better have one) */ if (state.lens[256] === 0) { strm.msg = 'invalid code -- missing end-of-block'; state.mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state.lenbits = 9; opts = { bits: state.lenbits }; ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed. // state.next_index = opts.table_index; state.lenbits = opts.bits; // state.lencode = state.next; if (ret) { strm.msg = 'invalid literal/lengths set'; state.mode = BAD; break; } state.distbits = 6; //state.distcode.copy(state.codes); // Switch to use dynamic table state.distcode = state.distdyn; opts = { bits: state.distbits }; ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed. // state.next_index = opts.table_index; state.distbits = opts.bits; // state.distcode = state.next; if (ret) { strm.msg = 'invalid distances set'; state.mode = BAD; break; } //Tracev((stderr, 'inflate: codes ok\n')); state.mode = LEN_; if (flush === Z_TREES) { break inf_leave; } /* falls through */ case LEN_: state.mode = LEN; /* falls through */ case LEN: if (have >= 6 && left >= 258) { //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- inflate_fast(strm, _out); //--- LOAD() --- put = strm.next_out; output = strm.output; left = strm.avail_out; next = strm.next_in; input = strm.input; have = strm.avail_in; hold = state.hold; bits = state.bits; //--- if (state.mode === TYPE) { state.back = -1; } break; } state.back = 0; for (;;) { here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if (here_bits <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if (here_op && (here_op & 0xf0) === 0) { last_bits = here_bits; last_op = here_op; last_val = here_val; for (;;) { here = state.lencode[last_val + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((last_bits + here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } //--- DROPBITS(last.bits) ---// hold >>>= last_bits; bits -= last_bits; //---// state.back += last_bits; } //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.back += here_bits; state.length = here_val; if (here_op === 0) { //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? // "inflate: literal '%c'\n" : // "inflate: literal 0x%02x\n", here.val)); state.mode = LIT; break; } if (here_op & 32) { //Tracevv((stderr, "inflate: end of block\n")); state.back = -1; state.mode = TYPE; break; } if (here_op & 64) { strm.msg = 'invalid literal/length code'; state.mode = BAD; break; } state.extra = here_op & 15; state.mode = LENEXT; /* falls through */ case LENEXT: if (state.extra) { //=== NEEDBITS(state.extra); n = state.extra; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; //--- DROPBITS(state.extra) ---// hold >>>= state.extra; bits -= state.extra; //---// state.back += state.extra; } //Tracevv((stderr, "inflate: length %u\n", state.length)); state.was = state.length; state.mode = DIST; /* falls through */ case DIST: for (;;) { here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } if ((here_op & 0xf0) === 0) { last_bits = here_bits; last_op = here_op; last_val = here_val; for (;;) { here = state.distcode[last_val + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; here_bits = here >>> 24; here_op = (here >>> 16) & 0xff; here_val = here & 0xffff; if ((last_bits + here_bits) <= bits) { break; } //--- PULLBYTE() ---// if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; //---// } //--- DROPBITS(last.bits) ---// hold >>>= last_bits; bits -= last_bits; //---// state.back += last_bits; } //--- DROPBITS(here.bits) ---// hold >>>= here_bits; bits -= here_bits; //---// state.back += here_bits; if (here_op & 64) { strm.msg = 'invalid distance code'; state.mode = BAD; break; } state.offset = here_val; state.extra = (here_op) & 15; state.mode = DISTEXT; /* falls through */ case DISTEXT: if (state.extra) { //=== NEEDBITS(state.extra); n = state.extra; while (bits < n) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; //--- DROPBITS(state.extra) ---// hold >>>= state.extra; bits -= state.extra; //---// state.back += state.extra; } //#ifdef INFLATE_STRICT if (state.offset > state.dmax) { strm.msg = 'invalid distance too far back'; state.mode = BAD; break; } //#endif //Tracevv((stderr, "inflate: distance %u\n", state.offset)); state.mode = MATCH; /* falls through */ case MATCH: if (left === 0) { break inf_leave; } copy = _out - left; if (state.offset > copy) { /* copy from window */ copy = state.offset - copy; if (copy > state.whave) { if (state.sane) { strm.msg = 'invalid distance too far back'; state.mode = BAD; break; } // (!) This block is disabled in zlib defaults, // don't enable it for binary compatibility //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR // Trace((stderr, "inflate.c too far\n")); // copy -= state.whave; // if (copy > state.length) { copy = state.length; } // if (copy > left) { copy = left; } // left -= copy; // state.length -= copy; // do { // output[put++] = 0; // } while (--copy); // if (state.length === 0) { state.mode = LEN; } // break; //#endif } if (copy > state.wnext) { copy -= state.wnext; from = state.wsize - copy; } else { from = state.wnext - copy; } if (copy > state.length) { copy = state.length; } from_source = state.window; } else { /* copy from output */ from_source = output; from = put - state.offset; copy = state.length; } if (copy > left) { copy = left; } left -= copy; state.length -= copy; do { output[put++] = from_source[from++]; } while (--copy); if (state.length === 0) { state.mode = LEN; } break; case LIT: if (left === 0) { break inf_leave; } output[put++] = state.length; left--; state.mode = LEN; break; case CHECK: if (state.wrap) { //=== NEEDBITS(32); while (bits < 32) { if (have === 0) { break inf_leave; } have--; // Use '|' instead of '+' to make sure that result is signed hold |= input[next++] << bits; bits += 8; } //===// _out -= left; strm.total_out += _out; state.total += _out; if (_out) { strm.adler = state.check = /*UPDATE(state.check, put - _out, _out);*/ (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out)); } _out = left; // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too if ((state.flags ? hold : zswap32(hold)) !== state.check) { strm.msg = 'incorrect data check'; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// //Tracev((stderr, "inflate: check matches trailer\n")); } state.mode = LENGTH; /* falls through */ case LENGTH: if (state.wrap && state.flags) { //=== NEEDBITS(32); while (bits < 32) { if (have === 0) { break inf_leave; } have--; hold += input[next++] << bits; bits += 8; } //===// if (hold !== (state.total & 0xffffffff)) { strm.msg = 'incorrect length check'; state.mode = BAD; break; } //=== INITBITS(); hold = 0; bits = 0; //===// //Tracev((stderr, "inflate: length matches trailer\n")); } state.mode = DONE; /* falls through */ case DONE: ret = Z_STREAM_END; break inf_leave; case BAD: ret = Z_DATA_ERROR; break inf_leave; case MEM: return Z_MEM_ERROR; case SYNC: /* falls through */ default: return Z_STREAM_ERROR; } } // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call updatewindow() to create and/or update the window state. Note: a memory error from inflate() is non-recoverable. */ //--- RESTORE() --- strm.next_out = put; strm.avail_out = left; strm.next_in = next; strm.avail_in = have; state.hold = hold; state.bits = bits; //--- if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && (state.mode < CHECK || flush !== Z_FINISH))) { if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) { state.mode = MEM; return Z_MEM_ERROR; } } _in -= strm.avail_in; _out -= strm.avail_out; strm.total_in += _in; strm.total_out += _out; state.total += _out; if (state.wrap && _out) { strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out)); } strm.data_type = state.bits + (state.last ? 64 : 0) + (state.mode === TYPE ? 128 : 0) + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) { ret = Z_BUF_ERROR; } return ret; } function inflateEnd(strm) { if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { return Z_STREAM_ERROR; } var state = strm.state; if (state.window) { state.window = null; } strm.state = null; return Z_OK; } function inflateGetHeader(strm, head) { var state; /* check state */ if (!strm || !strm.state) { return Z_STREAM_ERROR; } state = strm.state; if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; } /* save header structure */ state.head = head; head.done = false; return Z_OK; } function inflateSetDictionary(strm, dictionary) { var dictLength = dictionary.length; var state; var dictid; var ret; /* check state */ if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; } state = strm.state; if (state.wrap !== 0 && state.mode !== DICT) { return Z_STREAM_ERROR; } /* check for correct dictionary identifier */ if (state.mode === DICT) { dictid = 1; /* adler32(0, null, 0)*/ /* dictid = adler32(dictid, dictionary, dictLength); */ dictid = adler32(dictid, dictionary, dictLength, 0); if (dictid !== state.check) { return Z_DATA_ERROR; } } /* copy dictionary to window using updatewindow(), which will amend the existing dictionary if appropriate */ ret = updatewindow(strm, dictionary, dictLength, dictLength); if (ret) { state.mode = MEM; return Z_MEM_ERROR; } state.havedict = 1; // Tracev((stderr, "inflate: dictionary set\n")); return Z_OK; } exports.inflateReset = inflateReset; exports.inflateReset2 = inflateReset2; exports.inflateResetKeep = inflateResetKeep; exports.inflateInit = inflateInit; exports.inflateInit2 = inflateInit2; exports.inflate = inflate; exports.inflateEnd = inflateEnd; exports.inflateGetHeader = inflateGetHeader; exports.inflateSetDictionary = inflateSetDictionary; exports.inflateInfo = 'pako inflate (from Nodeca project)'; /* Not implemented exports.inflateCopy = inflateCopy; exports.inflateGetDictionary = inflateGetDictionary; exports.inflateMark = inflateMark; exports.inflatePrime = inflatePrime; exports.inflateSync = inflateSync; exports.inflateSyncPoint = inflateSyncPoint; exports.inflateUndermine = inflateUndermine; */ },{"../utils/common":188,"./adler32":189,"./crc32":191,"./inffast":193,"./inftrees":195}],195:[function(require,module,exports){ 'use strict'; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. var utils = require('../utils/common'); var MAXBITS = 15; var ENOUGH_LENS = 852; var ENOUGH_DISTS = 592; //var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); var CODES = 0; var LENS = 1; var DISTS = 2; var lbase = [ /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 ]; var lext = [ /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 ]; var dbase = [ /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 ]; var dext = [ /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64 ]; module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) { var bits = opts.bits; //here = opts.here; /* table entry for duplication */ var len = 0; /* a code's length in bits */ var sym = 0; /* index of code symbols */ var min = 0, max = 0; /* minimum and maximum code lengths */ var root = 0; /* number of index bits for root table */ var curr = 0; /* number of index bits for current table */ var drop = 0; /* code bits to drop for sub-table */ var left = 0; /* number of prefix codes available */ var used = 0; /* code entries in table used */ var huff = 0; /* Huffman code */ var incr; /* for incrementing code, index */ var fill; /* index for replicating entries */ var low; /* low bits for current root entry */ var mask; /* mask for low root bits */ var next; /* next available space in table */ var base = null; /* base value table to use */ var base_index = 0; // var shoextra; /* extra bits table to use */ var end; /* use base and extra for symbol > end */ var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ var extra = null; var extra_index = 0; var here_bits, here_op, here_val; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) { count[len] = 0; } for (sym = 0; sym < codes; sym++) { count[lens[lens_index + sym]]++; } /* bound code lengths, force root to be within code lengths */ root = bits; for (max = MAXBITS; max >= 1; max--) { if (count[max] !== 0) { break; } } if (root > max) { root = max; } if (max === 0) { /* no symbols to code at all */ //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ //table.bits[opts.table_index] = 1; //here.bits = (var char)1; //table.val[opts.table_index++] = 0; //here.val = (var short)0; table[table_index++] = (1 << 24) | (64 << 16) | 0; //table.op[opts.table_index] = 64; //table.bits[opts.table_index] = 1; //table.val[opts.table_index++] = 0; table[table_index++] = (1 << 24) | (64 << 16) | 0; opts.bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < max; min++) { if (count[min] !== 0) { break; } } if (root < min) { root = min; } /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) { return -1; } /* over-subscribed */ } if (left > 0 && (type === CODES || max !== 1)) { return -1; /* incomplete set */ } /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) { offs[len + 1] = offs[len] + count[len]; } /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) { if (lens[lens_index + sym] !== 0) { work[offs[lens[lens_index + sym]]++] = sym; } } /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftrees.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ // poor man optimization - use if-else instead of switch, // to avoid deopts in old v8 if (type === CODES) { base = extra = work; /* dummy value--not used */ end = 19; } else if (type === LENS) { base = lbase; base_index -= 257; extra = lext; extra_index -= 257; end = 256; } else { /* DISTS */ base = dbase; extra = dext; end = -1; } /* initialize opts for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = table_index; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = -1; /* trigger new sub-table when len > root */ used = 1 << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type === LENS && used > ENOUGH_LENS) || (type === DISTS && used > ENOUGH_DISTS)) { return 1; } /* process all codes and make table entries */ for (;;) { /* create table entry */ here_bits = len - drop; if (work[sym] < end) { here_op = 0; here_val = work[sym]; } else if (work[sym] > end) { here_op = extra[extra_index + work[sym]]; here_val = base[base_index + work[sym]]; } else { here_op = 32 + 64; /* end of block */ here_val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1 << (len - drop); fill = 1 << curr; min = fill; /* save offset to next table */ do { fill -= incr; table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; } while (fill !== 0); /* backwards increment the len-bit code huff */ incr = 1 << (len - 1); while (huff & incr) { incr >>= 1; } if (incr !== 0) { huff &= incr - 1; huff += incr; } else { huff = 0; } /* go to next symbol, update count, len */ sym++; if (--count[len] === 0) { if (len === max) { break; } len = lens[lens_index + work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) !== low) { /* if first time, transition to sub-tables */ if (drop === 0) { drop = root; } /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = 1 << curr; while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) { break; } curr++; left <<= 1; } /* check for enough space */ used += 1 << curr; if ((type === LENS && used > ENOUGH_LENS) || (type === DISTS && used > ENOUGH_DISTS)) { return 1; } /* point entry in root table to sub-table */ low = huff & mask; /*table.op[low] = curr; table.bits[low] = root; table.val[low] = next - opts.table_index;*/ table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; } } /* fill in remaining table entry if code is incomplete (guaranteed to have at most one remaining entry, since if the code is incomplete, the maximum code length that was allowed to get this far is one bit) */ if (huff !== 0) { //table.op[next + huff] = 64; /* invalid code marker */ //table.bits[next + huff] = len - drop; //table.val[next + huff] = 0; table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; } /* set return parameters */ //opts.table_index += used; opts.bits = root; return 0; }; },{"../utils/common":188}],196:[function(require,module,exports){ 'use strict'; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. module.exports = { 2: 'need dictionary', /* Z_NEED_DICT 2 */ 1: 'stream end', /* Z_STREAM_END 1 */ 0: '', /* Z_OK 0 */ '-1': 'file error', /* Z_ERRNO (-1) */ '-2': 'stream error', /* Z_STREAM_ERROR (-2) */ '-3': 'data error', /* Z_DATA_ERROR (-3) */ '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */ '-5': 'buffer error', /* Z_BUF_ERROR (-5) */ '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ }; },{}],197:[function(require,module,exports){ 'use strict'; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. var utils = require('../utils/common'); /* Public constants ==========================================================*/ /* ===========================================================================*/ //var Z_FILTERED = 1; //var Z_HUFFMAN_ONLY = 2; //var Z_RLE = 3; var Z_FIXED = 4; //var Z_DEFAULT_STRATEGY = 0; /* Possible values of the data_type field (though see inflate()) */ var Z_BINARY = 0; var Z_TEXT = 1; //var Z_ASCII = 1; // = Z_TEXT var Z_UNKNOWN = 2; /*============================================================================*/ function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } } // From zutil.h var STORED_BLOCK = 0; var STATIC_TREES = 1; var DYN_TREES = 2; /* The three kinds of block type */ var MIN_MATCH = 3; var MAX_MATCH = 258; /* The minimum and maximum match lengths */ // From deflate.h /* =========================================================================== * Internal compression state. */ var LENGTH_CODES = 29; /* number of length codes, not counting the special END_BLOCK code */ var LITERALS = 256; /* number of literal bytes 0..255 */ var L_CODES = LITERALS + 1 + LENGTH_CODES; /* number of Literal or Length codes, including the END_BLOCK code */ var D_CODES = 30; /* number of distance codes */ var BL_CODES = 19; /* number of codes used to transfer the bit lengths */ var HEAP_SIZE = 2 * L_CODES + 1; /* maximum heap size */ var MAX_BITS = 15; /* All codes must not exceed MAX_BITS bits */ var Buf_size = 16; /* size of bit buffer in bi_buf */ /* =========================================================================== * Constants */ var MAX_BL_BITS = 7; /* Bit length codes must not exceed MAX_BL_BITS bits */ var END_BLOCK = 256; /* end of block literal code */ var REP_3_6 = 16; /* repeat previous bit length 3-6 times (2 bits of repeat count) */ var REPZ_3_10 = 17; /* repeat a zero length 3-10 times (3 bits of repeat count) */ var REPZ_11_138 = 18; /* repeat a zero length 11-138 times (7 bits of repeat count) */ /* eslint-disable comma-spacing,array-bracket-spacing */ var extra_lbits = /* extra bits for each length code */ [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]; var extra_dbits = /* extra bits for each distance code */ [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]; var extra_blbits = /* extra bits for each bit length code */ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]; var bl_order = [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]; /* eslint-enable comma-spacing,array-bracket-spacing */ /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ /* =========================================================================== * Local data. These are initialized only once. */ // We pre-fill arrays with 0 to avoid uninitialized gaps var DIST_CODE_LEN = 512; /* see definition of array dist_code below */ // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 var static_ltree = new Array((L_CODES + 2) * 2); zero(static_ltree); /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ var static_dtree = new Array(D_CODES * 2); zero(static_dtree); /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ var _dist_code = new Array(DIST_CODE_LEN); zero(_dist_code); /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ var _length_code = new Array(MAX_MATCH - MIN_MATCH + 1); zero(_length_code); /* length code for each normalized match length (0 == MIN_MATCH) */ var base_length = new Array(LENGTH_CODES); zero(base_length); /* First normalized length for each code (0 = MIN_MATCH) */ var base_dist = new Array(D_CODES); zero(base_dist); /* First normalized distance for each code (0 = distance of 1) */ function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) { this.static_tree = static_tree; /* static tree or NULL */ this.extra_bits = extra_bits; /* extra bits for each code or NULL */ this.extra_base = extra_base; /* base index for extra_bits */ this.elems = elems; /* max number of elements in the tree */ this.max_length = max_length; /* max bit length for the codes */ // show if `static_tree` has data or dummy - needed for monomorphic objects this.has_stree = static_tree && static_tree.length; } var static_l_desc; var static_d_desc; var static_bl_desc; function TreeDesc(dyn_tree, stat_desc) { this.dyn_tree = dyn_tree; /* the dynamic tree */ this.max_code = 0; /* largest code with non zero frequency */ this.stat_desc = stat_desc; /* the corresponding static tree */ } function d_code(dist) { return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; } /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ function put_short(s, w) { // put_byte(s, (uch)((w) & 0xff)); // put_byte(s, (uch)((ush)(w) >> 8)); s.pending_buf[s.pending++] = (w) & 0xff; s.pending_buf[s.pending++] = (w >>> 8) & 0xff; } /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ function send_bits(s, value, length) { if (s.bi_valid > (Buf_size - length)) { s.bi_buf |= (value << s.bi_valid) & 0xffff; put_short(s, s.bi_buf); s.bi_buf = value >> (Buf_size - s.bi_valid); s.bi_valid += length - Buf_size; } else { s.bi_buf |= (value << s.bi_valid) & 0xffff; s.bi_valid += length; } } function send_code(s, c, tree) { send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/); } /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ function bi_reverse(code, len) { var res = 0; do { res |= code & 1; code >>>= 1; res <<= 1; } while (--len > 0); return res >>> 1; } /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ function bi_flush(s) { if (s.bi_valid === 16) { put_short(s, s.bi_buf); s.bi_buf = 0; s.bi_valid = 0; } else if (s.bi_valid >= 8) { s.pending_buf[s.pending++] = s.bi_buf & 0xff; s.bi_buf >>= 8; s.bi_valid -= 8; } } /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ function gen_bitlen(s, desc) // deflate_state *s; // tree_desc *desc; /* the tree descriptor */ { var tree = desc.dyn_tree; var max_code = desc.max_code; var stree = desc.stat_desc.static_tree; var has_stree = desc.stat_desc.has_stree; var extra = desc.stat_desc.extra_bits; var base = desc.stat_desc.extra_base; var max_length = desc.stat_desc.max_length; var h; /* heap index */ var n, m; /* iterate over the tree elements */ var bits; /* bit length */ var xbits; /* extra bits */ var f; /* frequency */ var overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS; bits++) { s.bl_count[bits] = 0; } /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */ for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { n = s.heap[h]; bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; if (bits > max_length) { bits = max_length; overflow++; } tree[n * 2 + 1]/*.Len*/ = bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) { continue; } /* not a leaf node */ s.bl_count[bits]++; xbits = 0; if (n >= base) { xbits = extra[n - base]; } f = tree[n * 2]/*.Freq*/; s.opt_len += f * (bits + xbits); if (has_stree) { s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits); } } if (overflow === 0) { return; } // Trace((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length - 1; while (s.bl_count[bits] === 0) { bits--; } s.bl_count[bits]--; /* move one leaf down the tree */ s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ s.bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from 'ar' written by Haruhiko Okumura.) */ for (bits = max_length; bits !== 0; bits--) { n = s.bl_count[bits]; while (n !== 0) { m = s.heap[--h]; if (m > max_code) { continue; } if (tree[m * 2 + 1]/*.Len*/ !== bits) { // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/; tree[m * 2 + 1]/*.Len*/ = bits; } n--; } } } /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ function gen_codes(tree, max_code, bl_count) // ct_data *tree; /* the tree to decorate */ // int max_code; /* largest code with non zero frequency */ // ushf *bl_count; /* number of codes at each bit length */ { var next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */ var code = 0; /* running code value */ var bits; /* bit index */ var n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { next_code[bits] = code = (code + bl_count[bits - 1]) << 1; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ //Assert (code + bl_count[MAX_BITS]-1 == (1< length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES - 1; code++) { base_length[code] = length; for (n = 0; n < (1 << extra_lbits[code]); n++) { _length_code[length++] = code; } } //Assert (length == 256, "tr_static_init: length != 256"); /* Note that the length 255 (match length 258) can be represented * in two different ways: code 284 + 5 bits or code 285, so we * overwrite length_code[255] to use the best encoding: */ _length_code[length - 1] = code; /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ dist = 0; for (code = 0; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1 << extra_dbits[code]); n++) { _dist_code[dist++] = code; } } //Assert (dist == 256, "tr_static_init: dist != 256"); dist >>= 7; /* from now on, all distances are divided by 128 */ for (; code < D_CODES; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { _dist_code[256 + dist++] = code; } } //Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) { bl_count[bits] = 0; } n = 0; while (n <= 143) { static_ltree[n * 2 + 1]/*.Len*/ = 8; n++; bl_count[8]++; } while (n <= 255) { static_ltree[n * 2 + 1]/*.Len*/ = 9; n++; bl_count[9]++; } while (n <= 279) { static_ltree[n * 2 + 1]/*.Len*/ = 7; n++; bl_count[7]++; } while (n <= 287) { static_ltree[n * 2 + 1]/*.Len*/ = 8; n++; bl_count[8]++; } /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes(static_ltree, L_CODES + 1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES; n++) { static_dtree[n * 2 + 1]/*.Len*/ = 5; static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5); } // Now data ready and we can init static trees static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); //static_init_done = true; } /* =========================================================================== * Initialize a new block. */ function init_block(s) { var n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1; s.opt_len = s.static_len = 0; s.last_lit = s.matches = 0; } /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ function bi_windup(s) { if (s.bi_valid > 8) { put_short(s, s.bi_buf); } else if (s.bi_valid > 0) { //put_byte(s, (Byte)s->bi_buf); s.pending_buf[s.pending++] = s.bi_buf; } s.bi_buf = 0; s.bi_valid = 0; } /* =========================================================================== * Copy a stored block, storing first the length and its * one's complement if requested. */ function copy_block(s, buf, len, header) //DeflateState *s; //charf *buf; /* the input data */ //unsigned len; /* its length */ //int header; /* true if block header must be written */ { bi_windup(s); /* align on byte boundary */ if (header) { put_short(s, len); put_short(s, ~len); } // while (len--) { // put_byte(s, *buf++); // } utils.arraySet(s.pending_buf, s.window, buf, len, s.pending); s.pending += len; } /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ function smaller(tree, n, m, depth) { var _n2 = n * 2; var _m2 = m * 2; return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); } /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ function pqdownheap(s, tree, k) // deflate_state *s; // ct_data *tree; /* the tree to restore */ // int k; /* node to move down */ { var v = s.heap[k]; var j = k << 1; /* left son of k */ while (j <= s.heap_len) { /* Set j to the smallest of the two sons: */ if (j < s.heap_len && smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s.heap[j], s.depth)) { break; } /* Exchange v with the smallest son */ s.heap[k] = s.heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s.heap[k] = v; } // inlined manually // var SMALLEST = 1; /* =========================================================================== * Send the block data compressed using the given Huffman trees */ function compress_block(s, ltree, dtree) // deflate_state *s; // const ct_data *ltree; /* literal tree */ // const ct_data *dtree; /* distance tree */ { var dist; /* distance of matched string */ var lc; /* match length or unmatched char (if dist == 0) */ var lx = 0; /* running index in l_buf */ var code; /* the code to send */ var extra; /* number of extra bits to send */ if (s.last_lit !== 0) { do { dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]); lc = s.pending_buf[s.l_buf + lx]; lx++; if (dist === 0) { send_code(s, lc, ltree); /* send a literal byte */ //Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; send_code(s, code + LITERALS + 1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra !== 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); //Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra !== 0) { dist -= base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, // "pendingBuf overflow"); } while (lx < s.last_lit); } send_code(s, END_BLOCK, ltree); } /* =========================================================================== * Construct one Huffman tree and assigns the code bit strings and lengths. * Update the total bit length for the current block. * IN assertion: the field freq is set for all tree elements. * OUT assertions: the fields len and code are set to the optimal bit length * and corresponding code. The length opt_len is updated; static_len is * also updated if stree is not null. The field max_code is set. */ function build_tree(s, desc) // deflate_state *s; // tree_desc *desc; /* the tree descriptor */ { var tree = desc.dyn_tree; var stree = desc.stat_desc.static_tree; var has_stree = desc.stat_desc.has_stree; var elems = desc.stat_desc.elems; var n, m; /* iterate over heap elements */ var max_code = -1; /* largest code with non zero frequency */ var node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s.heap_len = 0; s.heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { if (tree[n * 2]/*.Freq*/ !== 0) { s.heap[++s.heap_len] = max_code = n; s.depth[n] = 0; } else { tree[n * 2 + 1]/*.Len*/ = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s.heap_len < 2) { node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); tree[node * 2]/*.Freq*/ = 1; s.depth[node] = 0; s.opt_len--; if (has_stree) { s.static_len -= stree[node * 2 + 1]/*.Len*/; } /* node is 0 or 1 so it does not have extra bits */ } desc.max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); } /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { //pqremove(s, tree, n); /* n = node of least frequency */ /*** pqremove ***/ n = s.heap[1/*SMALLEST*/]; s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; pqdownheap(s, tree, 1/*SMALLEST*/); /***/ m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ s.heap[--s.heap_max] = m; /* Create a new node father of n and m */ tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node; /* and insert the new node in the heap */ s.heap[1/*SMALLEST*/] = node++; pqdownheap(s, tree, 1/*SMALLEST*/); } while (s.heap_len >= 2); s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, desc); /* The field len is now set, we can generate the bit codes */ gen_codes(tree, max_code, s.bl_count); } /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ function scan_tree(s, tree, max_code) // deflate_state *s; // ct_data *tree; /* the tree to be scanned */ // int max_code; /* and its largest code of non zero frequency */ { var n; /* iterates over all tree elements */ var prevlen = -1; /* last emitted length */ var curlen; /* length of current code */ var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ var count = 0; /* repeat count of the current code */ var max_count = 7; /* max repeat count */ var min_count = 4; /* min repeat count */ if (nextlen === 0) { max_count = 138; min_count = 3; } tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; if (++count < max_count && curlen === nextlen) { continue; } else if (count < min_count) { s.bl_tree[curlen * 2]/*.Freq*/ += count; } else if (curlen !== 0) { if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; } s.bl_tree[REP_3_6 * 2]/*.Freq*/++; } else if (count <= 10) { s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++; } else { s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++; } count = 0; prevlen = curlen; if (nextlen === 0) { max_count = 138; min_count = 3; } else if (curlen === nextlen) { max_count = 6; min_count = 3; } else { max_count = 7; min_count = 4; } } } /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ function send_tree(s, tree, max_code) // deflate_state *s; // ct_data *tree; /* the tree to be scanned */ // int max_code; /* and its largest code of non zero frequency */ { var n; /* iterates over all tree elements */ var prevlen = -1; /* last emitted length */ var curlen; /* length of current code */ var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ var count = 0; /* repeat count of the current code */ var max_count = 7; /* max repeat count */ var min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen === 0) { max_count = 138; min_count = 3; } for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; if (++count < max_count && curlen === nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s.bl_tree); } while (--count !== 0); } else if (curlen !== 0) { if (curlen !== prevlen) { send_code(s, curlen, s.bl_tree); count--; } //Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s.bl_tree); send_bits(s, count - 3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s.bl_tree); send_bits(s, count - 3, 3); } else { send_code(s, REPZ_11_138, s.bl_tree); send_bits(s, count - 11, 7); } count = 0; prevlen = curlen; if (nextlen === 0) { max_count = 138; min_count = 3; } else if (curlen === nextlen) { max_count = 6; min_count = 3; } else { max_count = 7; min_count = 4; } } } /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ function build_bl_tree(s) { var max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, s.dyn_ltree, s.l_desc.max_code); scan_tree(s, s.dyn_dtree, s.d_desc.max_code); /* Build the bit length tree: */ build_tree(s, s.bl_desc); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) { break; } } /* Update opt_len to include the bit length tree and counts */ s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", // s->opt_len, s->static_len)); return max_blindex; } /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ function send_all_trees(s, lcodes, dcodes, blcodes) // deflate_state *s; // int lcodes, dcodes, blcodes; /* number of codes for each tree */ { var rank; /* index in bl_order */ //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, // "too many codes"); //Tracev((stderr, "\nbl counts: ")); send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes - 1, 5); send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { //Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3); } //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */ //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */ //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } /* =========================================================================== * Check if the data type is TEXT or BINARY, using the following algorithm: * - TEXT if the two conditions below are satisfied: * a) There are no non-portable control characters belonging to the * "black list" (0..6, 14..25, 28..31). * b) There is at least one printable character belonging to the * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). * - BINARY otherwise. * - The following partially-portable control characters form a * "gray list" that is ignored in this detection algorithm: * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ function detect_data_type(s) { /* black_mask is the bit mask of black-listed bytes * set bits 0..6, 14..25, and 28..31 * 0xf3ffc07f = binary 11110011111111111100000001111111 */ var black_mask = 0xf3ffc07f; var n; /* Check for non-textual ("black-listed") bytes. */ for (n = 0; n <= 31; n++, black_mask >>>= 1) { if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) { return Z_BINARY; } } /* Check for textual ("white-listed") bytes. */ if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { return Z_TEXT; } for (n = 32; n < LITERALS; n++) { if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { return Z_TEXT; } } /* There are no "black-listed" or "white-listed" bytes: * this stream either is empty or has tolerated ("gray-listed") bytes only. */ return Z_BINARY; } var static_init_done = false; /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ function _tr_init(s) { if (!static_init_done) { tr_static_init(); static_init_done = true; } s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); s.bi_buf = 0; s.bi_valid = 0; /* Initialize the first block of the first file: */ init_block(s); } /* =========================================================================== * Send a stored block */ function _tr_stored_block(s, buf, stored_len, last) //DeflateState *s; //charf *buf; /* input block */ //ulg stored_len; /* length of input block */ //int last; /* one if this is the last block for a file */ { send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */ copy_block(s, buf, stored_len, true); /* with header */ } /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ function _tr_align(s) { send_bits(s, STATIC_TREES << 1, 3); send_code(s, END_BLOCK, static_ltree); bi_flush(s); } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and output the encoded block to the zip file. */ function _tr_flush_block(s, buf, stored_len, last) //DeflateState *s; //charf *buf; /* input block, or NULL if too old */ //ulg stored_len; /* length of input block */ //int last; /* one if this is the last block for a file */ { var opt_lenb, static_lenb; /* opt_len and static_len in bytes */ var max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s.level > 0) { /* Check if the file is binary or text */ if (s.strm.data_type === Z_UNKNOWN) { s.strm.data_type = detect_data_type(s); } /* Construct the literal and distance trees */ build_tree(s, s.l_desc); // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, // s->static_len)); build_tree(s, s.d_desc); // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, // s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ opt_lenb = (s.opt_len + 3 + 7) >>> 3; static_lenb = (s.static_len + 3 + 7) >>> 3; // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, // s->last_lit)); if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } } else { // Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) { /* 4: two words for the lengths */ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can't have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block(s, buf, stored_len, last); } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) { send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); compress_block(s, static_ltree, static_dtree); } else { send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1); compress_block(s, s.dyn_ltree, s.dyn_dtree); } // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); /* The above check is made mod 2^32, for files larger than 512 MB * and uLong implemented on 32 bits. */ init_block(s); if (last) { bi_windup(s); } // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, // s->compressed_len-7*last)); } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ function _tr_tally(s, dist, lc) // deflate_state *s; // unsigned dist; /* distance of matched string */ // unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ { //var out_length, in_length, dcode; s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; s.last_lit++; if (dist === 0) { /* lc is the unmatched char */ s.dyn_ltree[lc * 2]/*.Freq*/++; } else { s.matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ //Assert((ush)dist < (ush)MAX_DIST(s) && // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++; s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; } // (!) This block is disabled in zlib defaults, // don't enable it for binary compatibility //#ifdef TRUNCATE_BLOCK // /* Try to guess if it is profitable to stop the current block here */ // if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { // /* Compute an upper bound for the compressed length */ // out_length = s.last_lit*8; // in_length = s.strstart - s.block_start; // // for (dcode = 0; dcode < D_CODES; dcode++) { // out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); // } // out_length >>>= 3; // //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", // // s->last_lit, in_length, out_length, // // 100L - out_length*100L/in_length)); // if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { // return true; // } // } //#endif return (s.last_lit === s.lit_bufsize - 1); /* We avoid equality with lit_bufsize because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ } exports._tr_init = _tr_init; exports._tr_stored_block = _tr_stored_block; exports._tr_flush_block = _tr_flush_block; exports._tr_tally = _tr_tally; exports._tr_align = _tr_align; },{"../utils/common":188}],198:[function(require,module,exports){ 'use strict'; // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. function ZStream() { /* next input byte */ this.input = null; // JS specific, because we have no pointers this.next_in = 0; /* number of bytes available at input */ this.avail_in = 0; /* total number of input bytes read so far */ this.total_in = 0; /* next output byte should be put there */ this.output = null; // JS specific, because we have no pointers this.next_out = 0; /* remaining free space at output */ this.avail_out = 0; /* total number of bytes output so far */ this.total_out = 0; /* last error message, NULL if no error */ this.msg = ''/*Z_NULL*/; /* not visible by applications */ this.state = null; /* best guess about the data type: binary or text */ this.data_type = 2/*Z_UNKNOWN*/; /* adler32 value of the uncompressed data */ this.adler = 0; } module.exports = ZStream; },{}],199:[function(require,module,exports){ (function (process){ 'use strict'; if (!process.version || process.version.indexOf('v0.') === 0 || process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { module.exports = nextTick; } else { module.exports = process.nextTick; } function nextTick(fn, arg1, arg2, arg3) { if (typeof fn !== 'function') { throw new TypeError('"callback" argument must be a function'); } var len = arguments.length; var args, i; switch (len) { case 0: case 1: return process.nextTick(fn); case 2: return process.nextTick(function afterTickOne() { fn.call(null, arg1); }); case 3: return process.nextTick(function afterTickTwo() { fn.call(null, arg1, arg2); }); case 4: return process.nextTick(function afterTickThree() { fn.call(null, arg1, arg2, arg3); }); default: args = new Array(len - 1); i = 0; while (i < args.length) { args[i++] = arguments[i]; } return process.nextTick(function afterTick() { fn.apply(null, args); }); } } }).call(this,require('_process')) },{"_process":200}],200:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it // don't break things. But we need to wrap it in a try catch in case it is // wrapped in strict mode code which doesn't define any globals. It's inside a // function because try/catches deoptimize in certain engines. var cachedSetTimeout; var cachedClearTimeout; function defaultSetTimout() { throw new Error('setTimeout has not been defined'); } function defaultClearTimeout () { throw new Error('clearTimeout has not been defined'); } (function () { try { if (typeof setTimeout === 'function') { cachedSetTimeout = setTimeout; } else { cachedSetTimeout = defaultSetTimout; } } catch (e) { cachedSetTimeout = defaultSetTimout; } try { if (typeof clearTimeout === 'function') { cachedClearTimeout = clearTimeout; } else { cachedClearTimeout = defaultClearTimeout; } } catch (e) { cachedClearTimeout = defaultClearTimeout; } } ()) function runTimeout(fun) { if (cachedSetTimeout === setTimeout) { //normal enviroments in sane situations return setTimeout(fun, 0); } // if setTimeout wasn't available but was latter defined if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { cachedSetTimeout = setTimeout; return setTimeout(fun, 0); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedSetTimeout(fun, 0); } catch(e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedSetTimeout.call(null, fun, 0); } catch(e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error return cachedSetTimeout.call(this, fun, 0); } } } function runClearTimeout(marker) { if (cachedClearTimeout === clearTimeout) { //normal enviroments in sane situations return clearTimeout(marker); } // if clearTimeout wasn't available but was latter defined if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { cachedClearTimeout = clearTimeout; return clearTimeout(marker); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedClearTimeout(marker); } catch (e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedClearTimeout.call(null, marker); } catch (e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. // Some versions of I.E. have different rules for clearTimeout vs setTimeout return cachedClearTimeout.call(this, marker); } } } var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { if (!draining || !currentQueue) { return; } draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = runTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; runClearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { runTimeout(drainQueue); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.prependListener = noop; process.prependOnceListener = noop; process.listeners = function (name) { return [] } process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; },{}],201:[function(require,module,exports){ (function (global){ /*! https://mths.be/punycode v1.4.1 by @mathias */ ;(function(root) { /** Detect free variables */ var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; var freeModule = typeof module == 'object' && module && !module.nodeType && module; var freeGlobal = typeof global == 'object' && global; if ( freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal ) { root = freeGlobal; } /** * The `punycode` object. * @name punycode * @type Object */ var punycode, /** Highest positive signed 32-bit float value */ maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 /** Bootstring parameters */ base = 36, tMin = 1, tMax = 26, skew = 38, damp = 700, initialBias = 72, initialN = 128, // 0x80 delimiter = '-', // '\x2D' /** Regular expressions */ regexPunycode = /^xn--/, regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators /** Error messages */ errors = { 'overflow': 'Overflow: input needs wider integers to process', 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', 'invalid-input': 'Invalid input' }, /** Convenience shortcuts */ baseMinusTMin = base - tMin, floor = Math.floor, stringFromCharCode = String.fromCharCode, /** Temporary variable */ key; /*--------------------------------------------------------------------------*/ /** * A generic error utility function. * @private * @param {String} type The error type. * @returns {Error} Throws a `RangeError` with the applicable error message. */ function error(type) { throw new RangeError(errors[type]); } /** * A generic `Array#map` utility function. * @private * @param {Array} array The array to iterate over. * @param {Function} callback The function that gets called for every array * item. * @returns {Array} A new array of values returned by the callback function. */ function map(array, fn) { var length = array.length; var result = []; while (length--) { result[length] = fn(array[length]); } return result; } /** * A simple `Array#map`-like wrapper to work with domain name strings or email * addresses. * @private * @param {String} domain The domain name or email address. * @param {Function} callback The function that gets called for every * character. * @returns {Array} A new string of characters returned by the callback * function. */ function mapDomain(string, fn) { var parts = string.split('@'); var result = ''; if (parts.length > 1) { // In email addresses, only the domain name should be punycoded. Leave // the local part (i.e. everything up to `@`) intact. result = parts[0] + '@'; string = parts[1]; } // Avoid `split(regex)` for IE8 compatibility. See #17. string = string.replace(regexSeparators, '\x2E'); var labels = string.split('.'); var encoded = map(labels, fn).join('.'); return result + encoded; } /** * Creates an array containing the numeric code points of each Unicode * character in the string. While JavaScript uses UCS-2 internally, * this function will convert a pair of surrogate halves (each of which * UCS-2 exposes as separate characters) into a single code point, * matching UTF-16. * @see `punycode.ucs2.encode` * @see * @memberOf punycode.ucs2 * @name decode * @param {String} string The Unicode input string (UCS-2). * @returns {Array} The new array of code points. */ function ucs2decode(string) { var output = [], counter = 0, length = string.length, value, extra; while (counter < length) { value = string.charCodeAt(counter++); if (value >= 0xD800 && value <= 0xDBFF && counter < length) { // high surrogate, and there is a next character extra = string.charCodeAt(counter++); if ((extra & 0xFC00) == 0xDC00) { // low surrogate output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); } else { // unmatched surrogate; only append this code unit, in case the next // code unit is the high surrogate of a surrogate pair output.push(value); counter--; } } else { output.push(value); } } return output; } /** * Creates a string based on an array of numeric code points. * @see `punycode.ucs2.decode` * @memberOf punycode.ucs2 * @name encode * @param {Array} codePoints The array of numeric code points. * @returns {String} The new Unicode string (UCS-2). */ function ucs2encode(array) { return map(array, function(value) { var output = ''; if (value > 0xFFFF) { value -= 0x10000; output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); value = 0xDC00 | value & 0x3FF; } output += stringFromCharCode(value); return output; }).join(''); } /** * Converts a basic code point into a digit/integer. * @see `digitToBasic()` * @private * @param {Number} codePoint The basic numeric code point value. * @returns {Number} The numeric value of a basic code point (for use in * representing integers) in the range `0` to `base - 1`, or `base` if * the code point does not represent a value. */ function basicToDigit(codePoint) { if (codePoint - 48 < 10) { return codePoint - 22; } if (codePoint - 65 < 26) { return codePoint - 65; } if (codePoint - 97 < 26) { return codePoint - 97; } return base; } /** * Converts a digit/integer into a basic code point. * @see `basicToDigit()` * @private * @param {Number} digit The numeric value of a basic code point. * @returns {Number} The basic code point whose value (when used for * representing integers) is `digit`, which needs to be in the range * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is * used; else, the lowercase form is used. The behavior is undefined * if `flag` is non-zero and `digit` has no uppercase form. */ function digitToBasic(digit, flag) { // 0..25 map to ASCII a..z or A..Z // 26..35 map to ASCII 0..9 return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); } /** * Bias adaptation function as per section 3.4 of RFC 3492. * https://tools.ietf.org/html/rfc3492#section-3.4 * @private */ function adapt(delta, numPoints, firstTime) { var k = 0; delta = firstTime ? floor(delta / damp) : delta >> 1; delta += floor(delta / numPoints); for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { delta = floor(delta / baseMinusTMin); } return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); } /** * Converts a Punycode string of ASCII-only symbols to a string of Unicode * symbols. * @memberOf punycode * @param {String} input The Punycode string of ASCII-only symbols. * @returns {String} The resulting string of Unicode symbols. */ function decode(input) { // Don't use UCS-2 var output = [], inputLength = input.length, out, i = 0, n = initialN, bias = initialBias, basic, j, index, oldi, w, k, digit, t, /** Cached calculation results */ baseMinusT; // Handle the basic code points: let `basic` be the number of input code // points before the last delimiter, or `0` if there is none, then copy // the first basic code points to the output. basic = input.lastIndexOf(delimiter); if (basic < 0) { basic = 0; } for (j = 0; j < basic; ++j) { // if it's not a basic code point if (input.charCodeAt(j) >= 0x80) { error('not-basic'); } output.push(input.charCodeAt(j)); } // Main decoding loop: start just after the last delimiter if any basic code // points were copied; start at the beginning otherwise. for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { // `index` is the index of the next character to be consumed. // Decode a generalized variable-length integer into `delta`, // which gets added to `i`. The overflow checking is easier // if we increase `i` as we go, then subtract off its starting // value at the end to obtain `delta`. for (oldi = i, w = 1, k = base; /* no condition */; k += base) { if (index >= inputLength) { error('invalid-input'); } digit = basicToDigit(input.charCodeAt(index++)); if (digit >= base || digit > floor((maxInt - i) / w)) { error('overflow'); } i += digit * w; t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); if (digit < t) { break; } baseMinusT = base - t; if (w > floor(maxInt / baseMinusT)) { error('overflow'); } w *= baseMinusT; } out = output.length + 1; bias = adapt(i - oldi, out, oldi == 0); // `i` was supposed to wrap around from `out` to `0`, // incrementing `n` each time, so we'll fix that now: if (floor(i / out) > maxInt - n) { error('overflow'); } n += floor(i / out); i %= out; // Insert `n` at position `i` of the output output.splice(i++, 0, n); } return ucs2encode(output); } /** * Converts a string of Unicode symbols (e.g. a domain name label) to a * Punycode string of ASCII-only symbols. * @memberOf punycode * @param {String} input The string of Unicode symbols. * @returns {String} The resulting Punycode string of ASCII-only symbols. */ function encode(input) { var n, delta, handledCPCount, basicLength, bias, j, m, q, k, t, currentValue, output = [], /** `inputLength` will hold the number of code points in `input`. */ inputLength, /** Cached calculation results */ handledCPCountPlusOne, baseMinusT, qMinusT; // Convert the input in UCS-2 to Unicode input = ucs2decode(input); // Cache the length inputLength = input.length; // Initialize the state n = initialN; delta = 0; bias = initialBias; // Handle the basic code points for (j = 0; j < inputLength; ++j) { currentValue = input[j]; if (currentValue < 0x80) { output.push(stringFromCharCode(currentValue)); } } handledCPCount = basicLength = output.length; // `handledCPCount` is the number of code points that have been handled; // `basicLength` is the number of basic code points. // Finish the basic string - if it is not empty - with a delimiter if (basicLength) { output.push(delimiter); } // Main encoding loop: while (handledCPCount < inputLength) { // All non-basic code points < n have been handled already. Find the next // larger one: for (m = maxInt, j = 0; j < inputLength; ++j) { currentValue = input[j]; if (currentValue >= n && currentValue < m) { m = currentValue; } } // Increase `delta` enough to advance the decoder's state to , // but guard against overflow handledCPCountPlusOne = handledCPCount + 1; if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { error('overflow'); } delta += (m - n) * handledCPCountPlusOne; n = m; for (j = 0; j < inputLength; ++j) { currentValue = input[j]; if (currentValue < n && ++delta > maxInt) { error('overflow'); } if (currentValue == n) { // Represent delta as a generalized variable-length integer for (q = delta, k = base; /* no condition */; k += base) { t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); if (q < t) { break; } qMinusT = q - t; baseMinusT = base - t; output.push( stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) ); q = floor(qMinusT / baseMinusT); } output.push(stringFromCharCode(digitToBasic(q, 0))); bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); delta = 0; ++handledCPCount; } } ++delta; ++n; } return output.join(''); } /** * Converts a Punycode string representing a domain name or an email address * to Unicode. Only the Punycoded parts of the input will be converted, i.e. * it doesn't matter if you call it on a string that has already been * converted to Unicode. * @memberOf punycode * @param {String} input The Punycoded domain name or email address to * convert to Unicode. * @returns {String} The Unicode representation of the given Punycode * string. */ function toUnicode(input) { return mapDomain(input, function(string) { return regexPunycode.test(string) ? decode(string.slice(4).toLowerCase()) : string; }); } /** * Converts a Unicode string representing a domain name or an email address to * Punycode. Only the non-ASCII parts of the domain name will be converted, * i.e. it doesn't matter if you call it with a domain that's already in * ASCII. * @memberOf punycode * @param {String} input The domain name or email address to convert, as a * Unicode string. * @returns {String} The Punycode representation of the given domain name or * email address. */ function toASCII(input) { return mapDomain(input, function(string) { return regexNonASCII.test(string) ? 'xn--' + encode(string) : string; }); } /*--------------------------------------------------------------------------*/ /** Define the public API */ punycode = { /** * A string representing the current Punycode.js version number. * @memberOf punycode * @type String */ 'version': '1.4.1', /** * An object of methods to convert from JavaScript's internal character * representation (UCS-2) to Unicode code points, and back. * @see * @memberOf punycode * @type Object */ 'ucs2': { 'decode': ucs2decode, 'encode': ucs2encode }, 'decode': decode, 'encode': encode, 'toASCII': toASCII, 'toUnicode': toUnicode }; /** Expose `punycode` */ // Some AMD build optimizers, like r.js, check for specific condition patterns // like the following: if ( typeof define == 'function' && typeof define.amd == 'object' && define.amd ) { define('punycode', function() { return punycode; }); } else if (freeExports && freeModule) { if (module.exports == freeExports) { // in Node.js, io.js, or RingoJS v0.8.0+ freeModule.exports = punycode; } else { // in Narwhal or RingoJS v0.7.0- for (key in punycode) { punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); } } } else { // in Rhino or a web browser root.punycode = punycode; } }(this)); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],202:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; // If obj.hasOwnProperty has been overridden, then calling // obj.hasOwnProperty(prop) will break. // See: https://github.com/joyent/node/issues/1707 function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } module.exports = function(qs, sep, eq, options) { sep = sep || '&'; eq = eq || '='; var obj = {}; if (typeof qs !== 'string' || qs.length === 0) { return obj; } var regexp = /\+/g; qs = qs.split(sep); var maxKeys = 1000; if (options && typeof options.maxKeys === 'number') { maxKeys = options.maxKeys; } var len = qs.length; // maxKeys <= 0 means that we should not limit keys count if (maxKeys > 0 && len > maxKeys) { len = maxKeys; } for (var i = 0; i < len; ++i) { var x = qs[i].replace(regexp, '%20'), idx = x.indexOf(eq), kstr, vstr, k, v; if (idx >= 0) { kstr = x.substr(0, idx); vstr = x.substr(idx + 1); } else { kstr = x; vstr = ''; } k = decodeURIComponent(kstr); v = decodeURIComponent(vstr); if (!hasOwnProperty(obj, k)) { obj[k] = v; } else if (isArray(obj[k])) { obj[k].push(v); } else { obj[k] = [obj[k], v]; } } return obj; }; var isArray = Array.isArray || function (xs) { return Object.prototype.toString.call(xs) === '[object Array]'; }; },{}],203:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; var stringifyPrimitive = function(v) { switch (typeof v) { case 'string': return v; case 'boolean': return v ? 'true' : 'false'; case 'number': return isFinite(v) ? v : ''; default: return ''; } }; module.exports = function(obj, sep, eq, name) { sep = sep || '&'; eq = eq || '='; if (obj === null) { obj = undefined; } if (typeof obj === 'object') { return map(objectKeys(obj), function(k) { var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; if (isArray(obj[k])) { return map(obj[k], function(v) { return ks + encodeURIComponent(stringifyPrimitive(v)); }).join(sep); } else { return ks + encodeURIComponent(stringifyPrimitive(obj[k])); } }).join(sep); } if (!name) return ''; return encodeURIComponent(stringifyPrimitive(name)) + eq + encodeURIComponent(stringifyPrimitive(obj)); }; var isArray = Array.isArray || function (xs) { return Object.prototype.toString.call(xs) === '[object Array]'; }; function map (xs, f) { if (xs.map) return xs.map(f); var res = []; for (var i = 0; i < xs.length; i++) { res.push(f(xs[i], i)); } return res; } var objectKeys = Object.keys || function (obj) { var res = []; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key); } return res; }; },{}],204:[function(require,module,exports){ 'use strict'; exports.decode = exports.parse = require('./decode'); exports.encode = exports.stringify = require('./encode'); },{"./decode":202,"./encode":203}],205:[function(require,module,exports){ module.exports = require('./lib/_stream_duplex.js'); },{"./lib/_stream_duplex.js":206}],206:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // a duplex stream is just a stream that is both readable and writable. // Since JS doesn't have multiple prototypal inheritance, this class // prototypally inherits from Readable, and then parasitically from // Writable. 'use strict'; /**/ var processNextTick = require('process-nextick-args'); /**/ /**/ var objectKeys = Object.keys || function (obj) { var keys = []; for (var key in obj) { keys.push(key); }return keys; }; /**/ module.exports = Duplex; /**/ var util = require('core-util-is'); util.inherits = require('inherits'); /**/ var Readable = require('./_stream_readable'); var Writable = require('./_stream_writable'); util.inherits(Duplex, Readable); var keys = objectKeys(Writable.prototype); for (var v = 0; v < keys.length; v++) { var method = keys[v]; if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; } function Duplex(options) { if (!(this instanceof Duplex)) return new Duplex(options); Readable.call(this, options); Writable.call(this, options); if (options && options.readable === false) this.readable = false; if (options && options.writable === false) this.writable = false; this.allowHalfOpen = true; if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; this.once('end', onend); } // the no-half-open enforcer function onend() { // if we allow half-open state, or if the writable side ended, // then we're ok. if (this.allowHalfOpen || this._writableState.ended) return; // no more data can be written. // But allow more writes to happen in this tick. processNextTick(onEndNT, this); } function onEndNT(self) { self.end(); } Object.defineProperty(Duplex.prototype, 'destroyed', { get: function () { if (this._readableState === undefined || this._writableState === undefined) { return false; } return this._readableState.destroyed && this._writableState.destroyed; }, set: function (value) { // we ignore the value if the stream // has not been initialized yet if (this._readableState === undefined || this._writableState === undefined) { return; } // backward compatibility, the user is explicitly // managing destroyed this._readableState.destroyed = value; this._writableState.destroyed = value; } }); Duplex.prototype._destroy = function (err, cb) { this.push(null); this.end(); processNextTick(cb, err); }; function forEach(xs, f) { for (var i = 0, l = xs.length; i < l; i++) { f(xs[i], i); } } },{"./_stream_readable":208,"./_stream_writable":210,"core-util-is":181,"inherits":185,"process-nextick-args":199}],207:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // a passthrough stream. // basically just the most minimal sort of Transform stream. // Every written chunk gets output as-is. 'use strict'; module.exports = PassThrough; var Transform = require('./_stream_transform'); /**/ var util = require('core-util-is'); util.inherits = require('inherits'); /**/ util.inherits(PassThrough, Transform); function PassThrough(options) { if (!(this instanceof PassThrough)) return new PassThrough(options); Transform.call(this, options); } PassThrough.prototype._transform = function (chunk, encoding, cb) { cb(null, chunk); }; },{"./_stream_transform":209,"core-util-is":181,"inherits":185}],208:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; /**/ var processNextTick = require('process-nextick-args'); /**/ module.exports = Readable; /**/ var isArray = require('isarray'); /**/ /**/ var Duplex; /**/ Readable.ReadableState = ReadableState; /**/ var EE = require('events').EventEmitter; var EElistenerCount = function (emitter, type) { return emitter.listeners(type).length; }; /**/ /**/ var Stream = require('./internal/streams/stream'); /**/ // TODO(bmeurer): Change this back to const once hole checks are // properly optimized away early in Ignition+TurboFan. /**/ var Buffer = require('safe-buffer').Buffer; var OurUint8Array = global.Uint8Array || function () {}; function _uint8ArrayToBuffer(chunk) { return Buffer.from(chunk); } function _isUint8Array(obj) { return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; } /**/ /**/ var util = require('core-util-is'); util.inherits = require('inherits'); /**/ /**/ var debugUtil = require('util'); var debug = void 0; if (debugUtil && debugUtil.debuglog) { debug = debugUtil.debuglog('stream'); } else { debug = function () {}; } /**/ var BufferList = require('./internal/streams/BufferList'); var destroyImpl = require('./internal/streams/destroy'); var StringDecoder; util.inherits(Readable, Stream); var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; function prependListener(emitter, event, fn) { // Sadly this is not cacheable as some libraries bundle their own // event emitter implementation with them. if (typeof emitter.prependListener === 'function') { return emitter.prependListener(event, fn); } else { // This is a hack to make sure that our error handler is attached before any // userland ones. NEVER DO THIS. This is here only because this code needs // to continue to work with older versions of Node.js that do not include // the prependListener() method. The goal is to eventually remove this hack. if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; } } function ReadableState(options, stream) { Duplex = Duplex || require('./_stream_duplex'); options = options || {}; // object stream flag. Used to make read(n) ignore n and to // make all the buffer merging and length checks go away this.objectMode = !!options.objectMode; if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer // Note: 0 is a valid value, means "don't call _read preemptively ever" var hwm = options.highWaterMark; var defaultHwm = this.objectMode ? 16 : 16 * 1024; this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; // cast to ints. this.highWaterMark = Math.floor(this.highWaterMark); // A linked list is used to store data chunks instead of an array because the // linked list can remove elements from the beginning faster than // array.shift() this.buffer = new BufferList(); this.length = 0; this.pipes = null; this.pipesCount = 0; this.flowing = null; this.ended = false; this.endEmitted = false; this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted // immediately, or on a later tick. We set this to true at first, because // any actions that shouldn't happen until "later" should generally also // not happen before the first read call. this.sync = true; // whenever we return null, then we set a flag to say // that we're awaiting a 'readable' event emission. this.needReadable = false; this.emittedReadable = false; this.readableListening = false; this.resumeScheduled = false; // has it been destroyed this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string // encoding is 'binary' so we have to make this configurable. // Everything else in the universe uses 'utf8', though. this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled this.readingMore = false; this.decoder = null; this.encoding = null; if (options.encoding) { if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; this.decoder = new StringDecoder(options.encoding); this.encoding = options.encoding; } } function Readable(options) { Duplex = Duplex || require('./_stream_duplex'); if (!(this instanceof Readable)) return new Readable(options); this._readableState = new ReadableState(options, this); // legacy this.readable = true; if (options) { if (typeof options.read === 'function') this._read = options.read; if (typeof options.destroy === 'function') this._destroy = options.destroy; } Stream.call(this); } Object.defineProperty(Readable.prototype, 'destroyed', { get: function () { if (this._readableState === undefined) { return false; } return this._readableState.destroyed; }, set: function (value) { // we ignore the value if the stream // has not been initialized yet if (!this._readableState) { return; } // backward compatibility, the user is explicitly // managing destroyed this._readableState.destroyed = value; } }); Readable.prototype.destroy = destroyImpl.destroy; Readable.prototype._undestroy = destroyImpl.undestroy; Readable.prototype._destroy = function (err, cb) { this.push(null); cb(err); }; // Manually shove something into the read() buffer. // This returns true if the highWaterMark has not been hit yet, // similar to how Writable.write() returns true if you should // write() some more. Readable.prototype.push = function (chunk, encoding) { var state = this._readableState; var skipChunkCheck; if (!state.objectMode) { if (typeof chunk === 'string') { encoding = encoding || state.defaultEncoding; if (encoding !== state.encoding) { chunk = Buffer.from(chunk, encoding); encoding = ''; } skipChunkCheck = true; } } else { skipChunkCheck = true; } return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); }; // Unshift should *always* be something directly out of read() Readable.prototype.unshift = function (chunk) { return readableAddChunk(this, chunk, null, true, false); }; function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { var state = stream._readableState; if (chunk === null) { state.reading = false; onEofChunk(stream, state); } else { var er; if (!skipChunkCheck) er = chunkInvalid(state, chunk); if (er) { stream.emit('error', er); } else if (state.objectMode || chunk && chunk.length > 0) { if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { chunk = _uint8ArrayToBuffer(chunk); } if (addToFront) { if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true); } else if (state.ended) { stream.emit('error', new Error('stream.push() after EOF')); } else { state.reading = false; if (state.decoder && !encoding) { chunk = state.decoder.write(chunk); if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); } else { addChunk(stream, state, chunk, false); } } } else if (!addToFront) { state.reading = false; } } return needMoreData(state); } function addChunk(stream, state, chunk, addToFront) { if (state.flowing && state.length === 0 && !state.sync) { stream.emit('data', chunk); stream.read(0); } else { // update the buffer info. state.length += state.objectMode ? 1 : chunk.length; if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); if (state.needReadable) emitReadable(stream); } maybeReadMore(stream, state); } function chunkInvalid(state, chunk) { var er; if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { er = new TypeError('Invalid non-string/buffer chunk'); } return er; } // if it's past the high water mark, we can push in some more. // Also, if we have no data yet, we can stand some // more bytes. This is to work around cases where hwm=0, // such as the repl. Also, if the push() triggered a // readable event, and the user called read(largeNumber) such that // needReadable was set, then we ought to push more, so that another // 'readable' event will be triggered. function needMoreData(state) { return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0); } Readable.prototype.isPaused = function () { return this._readableState.flowing === false; }; // backwards compatibility. Readable.prototype.setEncoding = function (enc) { if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; this._readableState.decoder = new StringDecoder(enc); this._readableState.encoding = enc; return this; }; // Don't raise the hwm > 8MB var MAX_HWM = 0x800000; function computeNewHighWaterMark(n) { if (n >= MAX_HWM) { n = MAX_HWM; } else { // Get the next highest power of 2 to prevent increasing hwm excessively in // tiny amounts n--; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; n++; } return n; } // This function is designed to be inlinable, so please take care when making // changes to the function body. function howMuchToRead(n, state) { if (n <= 0 || state.length === 0 && state.ended) return 0; if (state.objectMode) return 1; if (n !== n) { // Only flow one buffer at a time if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; } // If we're asking for more than the current hwm, then raise the hwm. if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); if (n <= state.length) return n; // Don't have enough if (!state.ended) { state.needReadable = true; return 0; } return state.length; } // you can override either this method, or the async _read(n) below. Readable.prototype.read = function (n) { debug('read', n); n = parseInt(n, 10); var state = this._readableState; var nOrig = n; if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we // already have a bunch of data in the buffer, then just trigger // the 'readable' event and move on. if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) { debug('read: emitReadable', state.length, state.ended); if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); return null; } n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up. if (n === 0 && state.ended) { if (state.length === 0) endReadable(this); return null; } // All the actual chunk generation logic needs to be // *below* the call to _read. The reason is that in certain // synthetic stream cases, such as passthrough streams, _read // may be a completely synchronous operation which may change // the state of the read buffer, providing enough data when // before there was *not* enough. // // So, the steps are: // 1. Figure out what the state of things will be after we do // a read from the buffer. // // 2. If that resulting state will trigger a _read, then call _read. // Note that this may be asynchronous, or synchronous. Yes, it is // deeply ugly to write APIs this way, but that still doesn't mean // that the Readable class should behave improperly, as streams are // designed to be sync/async agnostic. // Take note if the _read call is sync or async (ie, if the read call // has returned yet), so that we know whether or not it's safe to emit // 'readable' etc. // // 3. Actually pull the requested chunks out of the buffer and return. // if we need a readable event, then we need to do some reading. var doRead = state.needReadable; debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some if (state.length === 0 || state.length - n < state.highWaterMark) { doRead = true; debug('length less than watermark', doRead); } // however, if we've ended, then there's no point, and if we're already // reading, then it's unnecessary. if (state.ended || state.reading) { doRead = false; debug('reading or ended', doRead); } else if (doRead) { debug('do read'); state.reading = true; state.sync = true; // if the length is currently zero, then we *need* a readable event. if (state.length === 0) state.needReadable = true; // call internal read method this._read(state.highWaterMark); state.sync = false; // If _read pushed data synchronously, then `reading` will be false, // and we need to re-evaluate how much data we can return to the user. if (!state.reading) n = howMuchToRead(nOrig, state); } var ret; if (n > 0) ret = fromList(n, state);else ret = null; if (ret === null) { state.needReadable = true; n = 0; } else { state.length -= n; } if (state.length === 0) { // If we have nothing in the buffer, then we want to know // as soon as we *do* get something into the buffer. if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick. if (nOrig !== n && state.ended) endReadable(this); } if (ret !== null) this.emit('data', ret); return ret; }; function onEofChunk(stream, state) { if (state.ended) return; if (state.decoder) { var chunk = state.decoder.end(); if (chunk && chunk.length) { state.buffer.push(chunk); state.length += state.objectMode ? 1 : chunk.length; } } state.ended = true; // emit 'readable' now to make sure it gets picked up. emitReadable(stream); } // Don't emit readable right away in sync mode, because this can trigger // another read() call => stack overflow. This way, it might trigger // a nextTick recursion warning, but that's not so bad. function emitReadable(stream) { var state = stream._readableState; state.needReadable = false; if (!state.emittedReadable) { debug('emitReadable', state.flowing); state.emittedReadable = true; if (state.sync) processNextTick(emitReadable_, stream);else emitReadable_(stream); } } function emitReadable_(stream) { debug('emit readable'); stream.emit('readable'); flow(stream); } // at this point, the user has presumably seen the 'readable' event, // and called read() to consume some data. that may have triggered // in turn another _read(n) call, in which case reading = true if // it's in progress. // However, if we're not ended, or reading, and the length < hwm, // then go ahead and try to read some more preemptively. function maybeReadMore(stream, state) { if (!state.readingMore) { state.readingMore = true; processNextTick(maybeReadMore_, stream, state); } } function maybeReadMore_(stream, state) { var len = state.length; while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) { debug('maybeReadMore read 0'); stream.read(0); if (len === state.length) // didn't get any data, stop spinning. break;else len = state.length; } state.readingMore = false; } // abstract method. to be overridden in specific implementation classes. // call cb(er, data) where data is <= n in length. // for virtual (non-string, non-buffer) streams, "length" is somewhat // arbitrary, and perhaps not very meaningful. Readable.prototype._read = function (n) { this.emit('error', new Error('_read() is not implemented')); }; Readable.prototype.pipe = function (dest, pipeOpts) { var src = this; var state = this._readableState; switch (state.pipesCount) { case 0: state.pipes = dest; break; case 1: state.pipes = [state.pipes, dest]; break; default: state.pipes.push(dest); break; } state.pipesCount += 1; debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; var endFn = doEnd ? onend : unpipe; if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn); dest.on('unpipe', onunpipe); function onunpipe(readable, unpipeInfo) { debug('onunpipe'); if (readable === src) { if (unpipeInfo && unpipeInfo.hasUnpiped === false) { unpipeInfo.hasUnpiped = true; cleanup(); } } } function onend() { debug('onend'); dest.end(); } // when the dest drains, it reduces the awaitDrain counter // on the source. This would be more elegant with a .once() // handler in flow(), but adding and removing repeatedly is // too slow. var ondrain = pipeOnDrain(src); dest.on('drain', ondrain); var cleanedUp = false; function cleanup() { debug('cleanup'); // cleanup event handlers once the pipe is broken dest.removeListener('close', onclose); dest.removeListener('finish', onfinish); dest.removeListener('drain', ondrain); dest.removeListener('error', onerror); dest.removeListener('unpipe', onunpipe); src.removeListener('end', onend); src.removeListener('end', unpipe); src.removeListener('data', ondata); cleanedUp = true; // if the reader is waiting for a drain event from this // specific writer, then it would cause it to never start // flowing again. // So, if this is awaiting a drain, then we just call it now. // If we don't know, then assume that we are waiting for one. if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); } // If the user pushes more data while we're writing to dest then we'll end up // in ondata again. However, we only want to increase awaitDrain once because // dest will only emit one 'drain' event for the multiple writes. // => Introduce a guard on increasing awaitDrain. var increasedAwaitDrain = false; src.on('data', ondata); function ondata(chunk) { debug('ondata'); increasedAwaitDrain = false; var ret = dest.write(chunk); if (false === ret && !increasedAwaitDrain) { // If the user unpiped during `dest.write()`, it is possible // to get stuck in a permanently paused state if that write // also returned false. // => Check whether `dest` is still a piping destination. if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { debug('false write response, pause', src._readableState.awaitDrain); src._readableState.awaitDrain++; increasedAwaitDrain = true; } src.pause(); } } // if the dest has an error, then stop piping into it. // however, don't suppress the throwing behavior for this. function onerror(er) { debug('onerror', er); unpipe(); dest.removeListener('error', onerror); if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er); } // Make sure our error handler is attached before userland ones. prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once. function onclose() { dest.removeListener('finish', onfinish); unpipe(); } dest.once('close', onclose); function onfinish() { debug('onfinish'); dest.removeListener('close', onclose); unpipe(); } dest.once('finish', onfinish); function unpipe() { debug('unpipe'); src.unpipe(dest); } // tell the dest that it's being piped to dest.emit('pipe', src); // start the flow if it hasn't been started already. if (!state.flowing) { debug('pipe resume'); src.resume(); } return dest; }; function pipeOnDrain(src) { return function () { var state = src._readableState; debug('pipeOnDrain', state.awaitDrain); if (state.awaitDrain) state.awaitDrain--; if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { state.flowing = true; flow(src); } }; } Readable.prototype.unpipe = function (dest) { var state = this._readableState; var unpipeInfo = { hasUnpiped: false }; // if we're not piping anywhere, then do nothing. if (state.pipesCount === 0) return this; // just one destination. most common case. if (state.pipesCount === 1) { // passed in one, but it's not the right one. if (dest && dest !== state.pipes) return this; if (!dest) dest = state.pipes; // got a match. state.pipes = null; state.pipesCount = 0; state.flowing = false; if (dest) dest.emit('unpipe', this, unpipeInfo); return this; } // slow case. multiple pipe destinations. if (!dest) { // remove all. var dests = state.pipes; var len = state.pipesCount; state.pipes = null; state.pipesCount = 0; state.flowing = false; for (var i = 0; i < len; i++) { dests[i].emit('unpipe', this, unpipeInfo); }return this; } // try to find the right one. var index = indexOf(state.pipes, dest); if (index === -1) return this; state.pipes.splice(index, 1); state.pipesCount -= 1; if (state.pipesCount === 1) state.pipes = state.pipes[0]; dest.emit('unpipe', this, unpipeInfo); return this; }; // set up data events if they are asked for // Ensure readable listeners eventually get something Readable.prototype.on = function (ev, fn) { var res = Stream.prototype.on.call(this, ev, fn); if (ev === 'data') { // Start flowing on next tick if stream isn't explicitly paused if (this._readableState.flowing !== false) this.resume(); } else if (ev === 'readable') { var state = this._readableState; if (!state.endEmitted && !state.readableListening) { state.readableListening = state.needReadable = true; state.emittedReadable = false; if (!state.reading) { processNextTick(nReadingNextTick, this); } else if (state.length) { emitReadable(this); } } } return res; }; Readable.prototype.addListener = Readable.prototype.on; function nReadingNextTick(self) { debug('readable nexttick read 0'); self.read(0); } // pause() and resume() are remnants of the legacy readable stream API // If the user uses them, then switch into old mode. Readable.prototype.resume = function () { var state = this._readableState; if (!state.flowing) { debug('resume'); state.flowing = true; resume(this, state); } return this; }; function resume(stream, state) { if (!state.resumeScheduled) { state.resumeScheduled = true; processNextTick(resume_, stream, state); } } function resume_(stream, state) { if (!state.reading) { debug('resume read 0'); stream.read(0); } state.resumeScheduled = false; state.awaitDrain = 0; stream.emit('resume'); flow(stream); if (state.flowing && !state.reading) stream.read(0); } Readable.prototype.pause = function () { debug('call pause flowing=%j', this._readableState.flowing); if (false !== this._readableState.flowing) { debug('pause'); this._readableState.flowing = false; this.emit('pause'); } return this; }; function flow(stream) { var state = stream._readableState; debug('flow', state.flowing); while (state.flowing && stream.read() !== null) {} } // wrap an old-style stream as the async data source. // This is *not* part of the readable stream interface. // It is an ugly unfortunate mess of history. Readable.prototype.wrap = function (stream) { var state = this._readableState; var paused = false; var self = this; stream.on('end', function () { debug('wrapped end'); if (state.decoder && !state.ended) { var chunk = state.decoder.end(); if (chunk && chunk.length) self.push(chunk); } self.push(null); }); stream.on('data', function (chunk) { debug('wrapped data'); if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; var ret = self.push(chunk); if (!ret) { paused = true; stream.pause(); } }); // proxy all the other methods. // important when wrapping filters and duplexes. for (var i in stream) { if (this[i] === undefined && typeof stream[i] === 'function') { this[i] = function (method) { return function () { return stream[method].apply(stream, arguments); }; }(i); } } // proxy certain important events. for (var n = 0; n < kProxyEvents.length; n++) { stream.on(kProxyEvents[n], self.emit.bind(self, kProxyEvents[n])); } // when we try to consume some more bytes, simply unpause the // underlying stream. self._read = function (n) { debug('wrapped _read', n); if (paused) { paused = false; stream.resume(); } }; return self; }; // exposed for testing purposes only. Readable._fromList = fromList; // Pluck off n bytes from an array of buffers. // Length is the combined lengths of all the buffers in the list. // This function is designed to be inlinable, so please take care when making // changes to the function body. function fromList(n, state) { // nothing buffered if (state.length === 0) return null; var ret; if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { // read it all, truncate the list if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length); state.buffer.clear(); } else { // read part of list ret = fromListPartial(n, state.buffer, state.decoder); } return ret; } // Extracts only enough buffered data to satisfy the amount requested. // This function is designed to be inlinable, so please take care when making // changes to the function body. function fromListPartial(n, list, hasStrings) { var ret; if (n < list.head.data.length) { // slice is the same for buffers and strings ret = list.head.data.slice(0, n); list.head.data = list.head.data.slice(n); } else if (n === list.head.data.length) { // first chunk is a perfect match ret = list.shift(); } else { // result spans more than one buffer ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list); } return ret; } // Copies a specified amount of characters from the list of buffered data // chunks. // This function is designed to be inlinable, so please take care when making // changes to the function body. function copyFromBufferString(n, list) { var p = list.head; var c = 1; var ret = p.data; n -= ret.length; while (p = p.next) { var str = p.data; var nb = n > str.length ? str.length : n; if (nb === str.length) ret += str;else ret += str.slice(0, n); n -= nb; if (n === 0) { if (nb === str.length) { ++c; if (p.next) list.head = p.next;else list.head = list.tail = null; } else { list.head = p; p.data = str.slice(nb); } break; } ++c; } list.length -= c; return ret; } // Copies a specified amount of bytes from the list of buffered data chunks. // This function is designed to be inlinable, so please take care when making // changes to the function body. function copyFromBuffer(n, list) { var ret = Buffer.allocUnsafe(n); var p = list.head; var c = 1; p.data.copy(ret); n -= p.data.length; while (p = p.next) { var buf = p.data; var nb = n > buf.length ? buf.length : n; buf.copy(ret, ret.length - n, 0, nb); n -= nb; if (n === 0) { if (nb === buf.length) { ++c; if (p.next) list.head = p.next;else list.head = list.tail = null; } else { list.head = p; p.data = buf.slice(nb); } break; } ++c; } list.length -= c; return ret; } function endReadable(stream) { var state = stream._readableState; // If we get here before consuming all the bytes, then that is a // bug in node. Should never happen. if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); if (!state.endEmitted) { state.ended = true; processNextTick(endReadableNT, state, stream); } } function endReadableNT(state, stream) { // Check that we didn't get one last unshift. if (!state.endEmitted && state.length === 0) { state.endEmitted = true; stream.readable = false; stream.emit('end'); } } function forEach(xs, f) { for (var i = 0, l = xs.length; i < l; i++) { f(xs[i], i); } } function indexOf(xs, x) { for (var i = 0, l = xs.length; i < l; i++) { if (xs[i] === x) return i; } return -1; } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./_stream_duplex":206,"./internal/streams/BufferList":211,"./internal/streams/destroy":212,"./internal/streams/stream":213,"_process":200,"core-util-is":181,"events":182,"inherits":185,"isarray":187,"process-nextick-args":199,"safe-buffer":218,"string_decoder/":224,"util":175}],209:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // a transform stream is a readable/writable stream where you do // something with the data. Sometimes it's called a "filter", // but that's not a great name for it, since that implies a thing where // some bits pass through, and others are simply ignored. (That would // be a valid example of a transform, of course.) // // While the output is causally related to the input, it's not a // necessarily symmetric or synchronous transformation. For example, // a zlib stream might take multiple plain-text writes(), and then // emit a single compressed chunk some time in the future. // // Here's how this works: // // The Transform stream has all the aspects of the readable and writable // stream classes. When you write(chunk), that calls _write(chunk,cb) // internally, and returns false if there's a lot of pending writes // buffered up. When you call read(), that calls _read(n) until // there's enough pending readable data buffered up. // // In a transform stream, the written data is placed in a buffer. When // _read(n) is called, it transforms the queued up data, calling the // buffered _write cb's as it consumes chunks. If consuming a single // written chunk would result in multiple output chunks, then the first // outputted bit calls the readcb, and subsequent chunks just go into // the read buffer, and will cause it to emit 'readable' if necessary. // // This way, back-pressure is actually determined by the reading side, // since _read has to be called to start processing a new chunk. However, // a pathological inflate type of transform can cause excessive buffering // here. For example, imagine a stream where every byte of input is // interpreted as an integer from 0-255, and then results in that many // bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in // 1kb of data being output. In this case, you could write a very small // amount of input, and end up with a very large amount of output. In // such a pathological inflating mechanism, there'd be no way to tell // the system to stop doing the transform. A single 4MB write could // cause the system to run out of memory. // // However, even in such a pathological case, only a single written chunk // would be consumed, and then the rest would wait (un-transformed) until // the results of the previous transformed chunk were consumed. 'use strict'; module.exports = Transform; var Duplex = require('./_stream_duplex'); /**/ var util = require('core-util-is'); util.inherits = require('inherits'); /**/ util.inherits(Transform, Duplex); function TransformState(stream) { this.afterTransform = function (er, data) { return afterTransform(stream, er, data); }; this.needTransform = false; this.transforming = false; this.writecb = null; this.writechunk = null; this.writeencoding = null; } function afterTransform(stream, er, data) { var ts = stream._transformState; ts.transforming = false; var cb = ts.writecb; if (!cb) { return stream.emit('error', new Error('write callback called multiple times')); } ts.writechunk = null; ts.writecb = null; if (data !== null && data !== undefined) stream.push(data); cb(er); var rs = stream._readableState; rs.reading = false; if (rs.needReadable || rs.length < rs.highWaterMark) { stream._read(rs.highWaterMark); } } function Transform(options) { if (!(this instanceof Transform)) return new Transform(options); Duplex.call(this, options); this._transformState = new TransformState(this); var stream = this; // start out asking for a readable event once data is transformed. this._readableState.needReadable = true; // we have implemented the _read method, and done the other things // that Readable wants before the first _read call, so unset the // sync guard flag. this._readableState.sync = false; if (options) { if (typeof options.transform === 'function') this._transform = options.transform; if (typeof options.flush === 'function') this._flush = options.flush; } // When the writable side finishes, then flush out anything remaining. this.once('prefinish', function () { if (typeof this._flush === 'function') this._flush(function (er, data) { done(stream, er, data); });else done(stream); }); } Transform.prototype.push = function (chunk, encoding) { this._transformState.needTransform = false; return Duplex.prototype.push.call(this, chunk, encoding); }; // This is the part where you do stuff! // override this function in implementation classes. // 'chunk' is an input chunk. // // Call `push(newChunk)` to pass along transformed output // to the readable side. You may call 'push' zero or more times. // // Call `cb(err)` when you are done with this chunk. If you pass // an error, then that'll put the hurt on the whole operation. If you // never call cb(), then you'll never get another chunk. Transform.prototype._transform = function (chunk, encoding, cb) { throw new Error('_transform() is not implemented'); }; Transform.prototype._write = function (chunk, encoding, cb) { var ts = this._transformState; ts.writecb = cb; ts.writechunk = chunk; ts.writeencoding = encoding; if (!ts.transforming) { var rs = this._readableState; if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); } }; // Doesn't matter what the args are here. // _transform does all the work. // That we got here means that the readable side wants more data. Transform.prototype._read = function (n) { var ts = this._transformState; if (ts.writechunk !== null && ts.writecb && !ts.transforming) { ts.transforming = true; this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); } else { // mark that we need a transform, so that any data that comes in // will get processed, now that we've asked for it. ts.needTransform = true; } }; Transform.prototype._destroy = function (err, cb) { var _this = this; Duplex.prototype._destroy.call(this, err, function (err2) { cb(err2); _this.emit('close'); }); }; function done(stream, er, data) { if (er) return stream.emit('error', er); if (data !== null && data !== undefined) stream.push(data); // if there's nothing in the write buffer, then that means // that nothing more will ever be provided var ws = stream._writableState; var ts = stream._transformState; if (ws.length) throw new Error('Calling transform done when ws.length != 0'); if (ts.transforming) throw new Error('Calling transform done when still transforming'); return stream.push(null); } },{"./_stream_duplex":206,"core-util-is":181,"inherits":185}],210:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // A bit simpler than readable streams. // Implement an async ._write(chunk, encoding, cb), and it'll handle all // the drain event emission and buffering. 'use strict'; /**/ var processNextTick = require('process-nextick-args'); /**/ module.exports = Writable; /* */ function WriteReq(chunk, encoding, cb) { this.chunk = chunk; this.encoding = encoding; this.callback = cb; this.next = null; } // It seems a linked list but it is not // there will be only 2 of these for each stream function CorkedRequest(state) { var _this = this; this.next = null; this.entry = null; this.finish = function () { onCorkedFinish(_this, state); }; } /* */ /**/ var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick; /**/ /**/ var Duplex; /**/ Writable.WritableState = WritableState; /**/ var util = require('core-util-is'); util.inherits = require('inherits'); /**/ /**/ var internalUtil = { deprecate: require('util-deprecate') }; /**/ /**/ var Stream = require('./internal/streams/stream'); /**/ /**/ var Buffer = require('safe-buffer').Buffer; var OurUint8Array = global.Uint8Array || function () {}; function _uint8ArrayToBuffer(chunk) { return Buffer.from(chunk); } function _isUint8Array(obj) { return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; } /**/ var destroyImpl = require('./internal/streams/destroy'); util.inherits(Writable, Stream); function nop() {} function WritableState(options, stream) { Duplex = Duplex || require('./_stream_duplex'); options = options || {}; // object stream flag to indicate whether or not this stream // contains buffers or objects. this.objectMode = !!options.objectMode; if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false // Note: 0 is a valid value, means that we always return false if // the entire buffer is not flushed immediately on write() var hwm = options.highWaterMark; var defaultHwm = this.objectMode ? 16 : 16 * 1024; this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; // cast to ints. this.highWaterMark = Math.floor(this.highWaterMark); // if _final has been called this.finalCalled = false; // drain event flag. this.needDrain = false; // at the start of calling end() this.ending = false; // when end() has been called, and returned this.ended = false; // when 'finish' is emitted this.finished = false; // has it been destroyed this.destroyed = false; // should we decode strings into buffers before passing to _write? // this is here so that some node-core streams can optimize string // handling at a lower level. var noDecode = options.decodeStrings === false; this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string // encoding is 'binary' so we have to make this configurable. // Everything else in the universe uses 'utf8', though. this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement // of how much we're waiting to get pushed to some underlying // socket or file. this.length = 0; // a flag to see when we're in the middle of a write. this.writing = false; // when true all writes will be buffered until .uncork() call this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately, // or on a later tick. We set this to true at first, because any // actions that shouldn't happen until "later" should generally also // not happen before the first write call. this.sync = true; // a flag to know if we're processing previously buffered items, which // may call the _write() callback in the same tick, so that we don't // end up in an overlapped onwrite situation. this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb) this.onwrite = function (er) { onwrite(stream, er); }; // the callback that the user supplies to write(chunk,encoding,cb) this.writecb = null; // the amount that is being written when _write is called. this.writelen = 0; this.bufferedRequest = null; this.lastBufferedRequest = null; // number of pending user-supplied write callbacks // this must be 0 before 'finish' can be emitted this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs // This is relevant for synchronous Transform streams this.prefinished = false; // True if the error was already emitted and should not be thrown again this.errorEmitted = false; // count buffered requests this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always // one allocated and free to use, and we maintain at most two this.corkedRequestsFree = new CorkedRequest(this); } WritableState.prototype.getBuffer = function getBuffer() { var current = this.bufferedRequest; var out = []; while (current) { out.push(current); current = current.next; } return out; }; (function () { try { Object.defineProperty(WritableState.prototype, 'buffer', { get: internalUtil.deprecate(function () { return this.getBuffer(); }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') }); } catch (_) {} })(); // Test _writableState for inheritance to account for Duplex streams, // whose prototype chain only points to Readable. var realHasInstance; if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { realHasInstance = Function.prototype[Symbol.hasInstance]; Object.defineProperty(Writable, Symbol.hasInstance, { value: function (object) { if (realHasInstance.call(this, object)) return true; return object && object._writableState instanceof WritableState; } }); } else { realHasInstance = function (object) { return object instanceof this; }; } function Writable(options) { Duplex = Duplex || require('./_stream_duplex'); // Writable ctor is applied to Duplexes, too. // `realHasInstance` is necessary because using plain `instanceof` // would return false, as no `_writableState` property is attached. // Trying to use the custom `instanceof` for Writable here will also break the // Node.js LazyTransform implementation, which has a non-trivial getter for // `_writableState` that would lead to infinite recursion. if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { return new Writable(options); } this._writableState = new WritableState(options, this); // legacy. this.writable = true; if (options) { if (typeof options.write === 'function') this._write = options.write; if (typeof options.writev === 'function') this._writev = options.writev; if (typeof options.destroy === 'function') this._destroy = options.destroy; if (typeof options.final === 'function') this._final = options.final; } Stream.call(this); } // Otherwise people can pipe Writable streams, which is just wrong. Writable.prototype.pipe = function () { this.emit('error', new Error('Cannot pipe, not readable')); }; function writeAfterEnd(stream, cb) { var er = new Error('write after end'); // TODO: defer error events consistently everywhere, not just the cb stream.emit('error', er); processNextTick(cb, er); } // Checks that a user-supplied chunk is valid, especially for the particular // mode the stream is in. Currently this means that `null` is never accepted // and undefined/non-string values are only allowed in object mode. function validChunk(stream, state, chunk, cb) { var valid = true; var er = false; if (chunk === null) { er = new TypeError('May not write null values to stream'); } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { er = new TypeError('Invalid non-string/buffer chunk'); } if (er) { stream.emit('error', er); processNextTick(cb, er); valid = false; } return valid; } Writable.prototype.write = function (chunk, encoding, cb) { var state = this._writableState; var ret = false; var isBuf = _isUint8Array(chunk) && !state.objectMode; if (isBuf && !Buffer.isBuffer(chunk)) { chunk = _uint8ArrayToBuffer(chunk); } if (typeof encoding === 'function') { cb = encoding; encoding = null; } if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; if (typeof cb !== 'function') cb = nop; if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { state.pendingcb++; ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); } return ret; }; Writable.prototype.cork = function () { var state = this._writableState; state.corked++; }; Writable.prototype.uncork = function () { var state = this._writableState; if (state.corked) { state.corked--; if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); } }; Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { // node::ParseEncoding() requires lower case. if (typeof encoding === 'string') encoding = encoding.toLowerCase(); if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); this._writableState.defaultEncoding = encoding; return this; }; function decodeChunk(state, chunk, encoding) { if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { chunk = Buffer.from(chunk, encoding); } return chunk; } // if we're already writing something, then just put this // in the queue, and wait our turn. Otherwise, call _write // If we return false, then we need a drain event, so set that flag. function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { if (!isBuf) { var newChunk = decodeChunk(state, chunk, encoding); if (chunk !== newChunk) { isBuf = true; encoding = 'buffer'; chunk = newChunk; } } var len = state.objectMode ? 1 : chunk.length; state.length += len; var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false. if (!ret) state.needDrain = true; if (state.writing || state.corked) { var last = state.lastBufferedRequest; state.lastBufferedRequest = { chunk: chunk, encoding: encoding, isBuf: isBuf, callback: cb, next: null }; if (last) { last.next = state.lastBufferedRequest; } else { state.bufferedRequest = state.lastBufferedRequest; } state.bufferedRequestCount += 1; } else { doWrite(stream, state, false, len, chunk, encoding, cb); } return ret; } function doWrite(stream, state, writev, len, chunk, encoding, cb) { state.writelen = len; state.writecb = cb; state.writing = true; state.sync = true; if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); state.sync = false; } function onwriteError(stream, state, sync, er, cb) { --state.pendingcb; if (sync) { // defer the callback if we are being called synchronously // to avoid piling up things on the stack processNextTick(cb, er); // this can emit finish, and it will always happen // after error processNextTick(finishMaybe, stream, state); stream._writableState.errorEmitted = true; stream.emit('error', er); } else { // the caller expect this to happen before if // it is async cb(er); stream._writableState.errorEmitted = true; stream.emit('error', er); // this can emit finish, but finish must // always follow error finishMaybe(stream, state); } } function onwriteStateUpdate(state) { state.writing = false; state.writecb = null; state.length -= state.writelen; state.writelen = 0; } function onwrite(stream, er) { var state = stream._writableState; var sync = state.sync; var cb = state.writecb; onwriteStateUpdate(state); if (er) onwriteError(stream, state, sync, er, cb);else { // Check if we're actually ready to finish, but don't emit yet var finished = needFinish(state); if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { clearBuffer(stream, state); } if (sync) { /**/ asyncWrite(afterWrite, stream, state, finished, cb); /**/ } else { afterWrite(stream, state, finished, cb); } } } function afterWrite(stream, state, finished, cb) { if (!finished) onwriteDrain(stream, state); state.pendingcb--; cb(); finishMaybe(stream, state); } // Must force callback to be called on nextTick, so that we don't // emit 'drain' before the write() consumer gets the 'false' return // value, and has a chance to attach a 'drain' listener. function onwriteDrain(stream, state) { if (state.length === 0 && state.needDrain) { state.needDrain = false; stream.emit('drain'); } } // if there's something in the buffer waiting, then process it function clearBuffer(stream, state) { state.bufferProcessing = true; var entry = state.bufferedRequest; if (stream._writev && entry && entry.next) { // Fast case, write everything using _writev() var l = state.bufferedRequestCount; var buffer = new Array(l); var holder = state.corkedRequestsFree; holder.entry = entry; var count = 0; var allBuffers = true; while (entry) { buffer[count] = entry; if (!entry.isBuf) allBuffers = false; entry = entry.next; count += 1; } buffer.allBuffers = allBuffers; doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time // as the hot path ends with doWrite state.pendingcb++; state.lastBufferedRequest = null; if (holder.next) { state.corkedRequestsFree = holder.next; holder.next = null; } else { state.corkedRequestsFree = new CorkedRequest(state); } } else { // Slow case, write chunks one-by-one while (entry) { var chunk = entry.chunk; var encoding = entry.encoding; var cb = entry.callback; var len = state.objectMode ? 1 : chunk.length; doWrite(stream, state, false, len, chunk, encoding, cb); entry = entry.next; // if we didn't call the onwrite immediately, then // it means that we need to wait until it does. // also, that means that the chunk and cb are currently // being processed, so move the buffer counter past them. if (state.writing) { break; } } if (entry === null) state.lastBufferedRequest = null; } state.bufferedRequestCount = 0; state.bufferedRequest = entry; state.bufferProcessing = false; } Writable.prototype._write = function (chunk, encoding, cb) { cb(new Error('_write() is not implemented')); }; Writable.prototype._writev = null; Writable.prototype.end = function (chunk, encoding, cb) { var state = this._writableState; if (typeof chunk === 'function') { cb = chunk; chunk = null; encoding = null; } else if (typeof encoding === 'function') { cb = encoding; encoding = null; } if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks if (state.corked) { state.corked = 1; this.uncork(); } // ignore unnecessary end() calls. if (!state.ending && !state.finished) endWritable(this, state, cb); }; function needFinish(state) { return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; } function callFinal(stream, state) { stream._final(function (err) { state.pendingcb--; if (err) { stream.emit('error', err); } state.prefinished = true; stream.emit('prefinish'); finishMaybe(stream, state); }); } function prefinish(stream, state) { if (!state.prefinished && !state.finalCalled) { if (typeof stream._final === 'function') { state.pendingcb++; state.finalCalled = true; processNextTick(callFinal, stream, state); } else { state.prefinished = true; stream.emit('prefinish'); } } } function finishMaybe(stream, state) { var need = needFinish(state); if (need) { prefinish(stream, state); if (state.pendingcb === 0) { state.finished = true; stream.emit('finish'); } } return need; } function endWritable(stream, state, cb) { state.ending = true; finishMaybe(stream, state); if (cb) { if (state.finished) processNextTick(cb);else stream.once('finish', cb); } state.ended = true; stream.writable = false; } function onCorkedFinish(corkReq, state, err) { var entry = corkReq.entry; corkReq.entry = null; while (entry) { var cb = entry.callback; state.pendingcb--; cb(err); entry = entry.next; } if (state.corkedRequestsFree) { state.corkedRequestsFree.next = corkReq; } else { state.corkedRequestsFree = corkReq; } } Object.defineProperty(Writable.prototype, 'destroyed', { get: function () { if (this._writableState === undefined) { return false; } return this._writableState.destroyed; }, set: function (value) { // we ignore the value if the stream // has not been initialized yet if (!this._writableState) { return; } // backward compatibility, the user is explicitly // managing destroyed this._writableState.destroyed = value; } }); Writable.prototype.destroy = destroyImpl.destroy; Writable.prototype._undestroy = destroyImpl.undestroy; Writable.prototype._destroy = function (err, cb) { this.end(); cb(err); }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./_stream_duplex":206,"./internal/streams/destroy":212,"./internal/streams/stream":213,"_process":200,"core-util-is":181,"inherits":185,"process-nextick-args":199,"safe-buffer":218,"util-deprecate":228}],211:[function(require,module,exports){ 'use strict'; /**/ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Buffer = require('safe-buffer').Buffer; /**/ function copyBuffer(src, target, offset) { src.copy(target, offset); } module.exports = function () { function BufferList() { _classCallCheck(this, BufferList); this.head = null; this.tail = null; this.length = 0; } BufferList.prototype.push = function push(v) { var entry = { data: v, next: null }; if (this.length > 0) this.tail.next = entry;else this.head = entry; this.tail = entry; ++this.length; }; BufferList.prototype.unshift = function unshift(v) { var entry = { data: v, next: this.head }; if (this.length === 0) this.tail = entry; this.head = entry; ++this.length; }; BufferList.prototype.shift = function shift() { if (this.length === 0) return; var ret = this.head.data; if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; --this.length; return ret; }; BufferList.prototype.clear = function clear() { this.head = this.tail = null; this.length = 0; }; BufferList.prototype.join = function join(s) { if (this.length === 0) return ''; var p = this.head; var ret = '' + p.data; while (p = p.next) { ret += s + p.data; }return ret; }; BufferList.prototype.concat = function concat(n) { if (this.length === 0) return Buffer.alloc(0); if (this.length === 1) return this.head.data; var ret = Buffer.allocUnsafe(n >>> 0); var p = this.head; var i = 0; while (p) { copyBuffer(p.data, ret, i); i += p.data.length; p = p.next; } return ret; }; return BufferList; }(); },{"safe-buffer":218}],212:[function(require,module,exports){ 'use strict'; /**/ var processNextTick = require('process-nextick-args'); /**/ // undocumented cb() API, needed for core, not for public API function destroy(err, cb) { var _this = this; var readableDestroyed = this._readableState && this._readableState.destroyed; var writableDestroyed = this._writableState && this._writableState.destroyed; if (readableDestroyed || writableDestroyed) { if (cb) { cb(err); } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { processNextTick(emitErrorNT, this, err); } return; } // we set destroyed to true before firing error callbacks in order // to make it re-entrance safe in case destroy() is called within callbacks if (this._readableState) { this._readableState.destroyed = true; } // if this is a duplex stream mark the writable part as destroyed as well if (this._writableState) { this._writableState.destroyed = true; } this._destroy(err || null, function (err) { if (!cb && err) { processNextTick(emitErrorNT, _this, err); if (_this._writableState) { _this._writableState.errorEmitted = true; } } else if (cb) { cb(err); } }); } function undestroy() { if (this._readableState) { this._readableState.destroyed = false; this._readableState.reading = false; this._readableState.ended = false; this._readableState.endEmitted = false; } if (this._writableState) { this._writableState.destroyed = false; this._writableState.ended = false; this._writableState.ending = false; this._writableState.finished = false; this._writableState.errorEmitted = false; } } function emitErrorNT(self, err) { self.emit('error', err); } module.exports = { destroy: destroy, undestroy: undestroy }; },{"process-nextick-args":199}],213:[function(require,module,exports){ module.exports = require('events').EventEmitter; },{"events":182}],214:[function(require,module,exports){ module.exports = require('./readable').PassThrough },{"./readable":215}],215:[function(require,module,exports){ exports = module.exports = require('./lib/_stream_readable.js'); exports.Stream = exports; exports.Readable = exports; exports.Writable = require('./lib/_stream_writable.js'); exports.Duplex = require('./lib/_stream_duplex.js'); exports.Transform = require('./lib/_stream_transform.js'); exports.PassThrough = require('./lib/_stream_passthrough.js'); },{"./lib/_stream_duplex.js":206,"./lib/_stream_passthrough.js":207,"./lib/_stream_readable.js":208,"./lib/_stream_transform.js":209,"./lib/_stream_writable.js":210}],216:[function(require,module,exports){ module.exports = require('./readable').Transform },{"./readable":215}],217:[function(require,module,exports){ module.exports = require('./lib/_stream_writable.js'); },{"./lib/_stream_writable.js":210}],218:[function(require,module,exports){ /* eslint-disable node/no-deprecated-api */ var buffer = require('buffer') var Buffer = buffer.Buffer // alternative to using Object.keys for old browsers function copyProps (src, dst) { for (var key in src) { dst[key] = src[key] } } if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { module.exports = buffer } else { // Copy properties from require('buffer') copyProps(buffer, exports) exports.Buffer = SafeBuffer } function SafeBuffer (arg, encodingOrOffset, length) { return Buffer(arg, encodingOrOffset, length) } // Copy static methods from Buffer copyProps(Buffer, SafeBuffer) SafeBuffer.from = function (arg, encodingOrOffset, length) { if (typeof arg === 'number') { throw new TypeError('Argument must not be a number') } return Buffer(arg, encodingOrOffset, length) } SafeBuffer.alloc = function (size, fill, encoding) { if (typeof size !== 'number') { throw new TypeError('Argument must be a number') } var buf = Buffer(size) if (fill !== undefined) { if (typeof encoding === 'string') { buf.fill(fill, encoding) } else { buf.fill(fill) } } else { buf.fill(0) } return buf } SafeBuffer.allocUnsafe = function (size) { if (typeof size !== 'number') { throw new TypeError('Argument must be a number') } return Buffer(size) } SafeBuffer.allocUnsafeSlow = function (size) { if (typeof size !== 'number') { throw new TypeError('Argument must be a number') } return buffer.SlowBuffer(size) } },{"buffer":178}],219:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. module.exports = Stream; var EE = require('events').EventEmitter; var inherits = require('inherits'); inherits(Stream, EE); Stream.Readable = require('readable-stream/readable.js'); Stream.Writable = require('readable-stream/writable.js'); Stream.Duplex = require('readable-stream/duplex.js'); Stream.Transform = require('readable-stream/transform.js'); Stream.PassThrough = require('readable-stream/passthrough.js'); // Backwards-compat with node 0.4.x Stream.Stream = Stream; // old-style streams. Note that the pipe method (the only relevant // part of this class) is overridden in the Readable class. function Stream() { EE.call(this); } Stream.prototype.pipe = function(dest, options) { var source = this; function ondata(chunk) { if (dest.writable) { if (false === dest.write(chunk) && source.pause) { source.pause(); } } } source.on('data', ondata); function ondrain() { if (source.readable && source.resume) { source.resume(); } } dest.on('drain', ondrain); // If the 'end' option is not supplied, dest.end() will be called when // source gets the 'end' or 'close' events. Only dest.end() once. if (!dest._isStdio && (!options || options.end !== false)) { source.on('end', onend); source.on('close', onclose); } var didOnEnd = false; function onend() { if (didOnEnd) return; didOnEnd = true; dest.end(); } function onclose() { if (didOnEnd) return; didOnEnd = true; if (typeof dest.destroy === 'function') dest.destroy(); } // don't leave dangling pipes when there are errors. function onerror(er) { cleanup(); if (EE.listenerCount(this, 'error') === 0) { throw er; // Unhandled stream error in pipe. } } source.on('error', onerror); dest.on('error', onerror); // remove all the event listeners that were added. function cleanup() { source.removeListener('data', ondata); dest.removeListener('drain', ondrain); source.removeListener('end', onend); source.removeListener('close', onclose); source.removeListener('error', onerror); dest.removeListener('error', onerror); source.removeListener('end', cleanup); source.removeListener('close', cleanup); dest.removeListener('close', cleanup); } source.on('end', cleanup); source.on('close', cleanup); dest.on('close', cleanup); dest.emit('pipe', source); // Allow for unix-like usage: A.pipe(B).pipe(C) return dest; }; },{"events":182,"inherits":185,"readable-stream/duplex.js":205,"readable-stream/passthrough.js":214,"readable-stream/readable.js":215,"readable-stream/transform.js":216,"readable-stream/writable.js":217}],220:[function(require,module,exports){ (function (global){ var ClientRequest = require('./lib/request') var extend = require('xtend') var statusCodes = require('builtin-status-codes') var url = require('url') var http = exports http.request = function (opts, cb) { if (typeof opts === 'string') opts = url.parse(opts) else opts = extend(opts) // Normally, the page is loaded from http or https, so not specifying a protocol // will result in a (valid) protocol-relative url. However, this won't work if // the protocol is something else, like 'file:' var defaultProtocol = global.location.protocol.search(/^https?:$/) === -1 ? 'http:' : '' var protocol = opts.protocol || defaultProtocol var host = opts.hostname || opts.host var port = opts.port var path = opts.path || '/' // Necessary for IPv6 addresses if (host && host.indexOf(':') !== -1) host = '[' + host + ']' // This may be a relative url. The browser should always be able to interpret it correctly. opts.url = (host ? (protocol + '//' + host) : '') + (port ? ':' + port : '') + path opts.method = (opts.method || 'GET').toUpperCase() opts.headers = opts.headers || {} // Also valid opts.auth, opts.mode var req = new ClientRequest(opts) if (cb) req.on('response', cb) return req } http.get = function get (opts, cb) { var req = http.request(opts, cb) req.end() return req } http.Agent = function () {} http.Agent.defaultMaxSockets = 4 http.STATUS_CODES = statusCodes http.METHODS = [ 'CHECKOUT', 'CONNECT', 'COPY', 'DELETE', 'GET', 'HEAD', 'LOCK', 'M-SEARCH', 'MERGE', 'MKACTIVITY', 'MKCOL', 'MOVE', 'NOTIFY', 'OPTIONS', 'PATCH', 'POST', 'PROPFIND', 'PROPPATCH', 'PURGE', 'PUT', 'REPORT', 'SEARCH', 'SUBSCRIBE', 'TRACE', 'UNLOCK', 'UNSUBSCRIBE' ] }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./lib/request":222,"builtin-status-codes":179,"url":226,"xtend":232}],221:[function(require,module,exports){ (function (global){ exports.fetch = isFunction(global.fetch) && isFunction(global.ReadableStream) exports.blobConstructor = false try { new Blob([new ArrayBuffer(1)]) exports.blobConstructor = true } catch (e) {} // The xhr request to example.com may violate some restrictive CSP configurations, // so if we're running in a browser that supports `fetch`, avoid calling getXHR() // and assume support for certain features below. var xhr function getXHR () { // Cache the xhr value if (xhr !== undefined) return xhr if (global.XMLHttpRequest) { xhr = new global.XMLHttpRequest() // If XDomainRequest is available (ie only, where xhr might not work // cross domain), use the page location. Otherwise use example.com // Note: this doesn't actually make an http request. try { xhr.open('GET', global.XDomainRequest ? '/' : 'https://example.com') } catch(e) { xhr = null } } else { // Service workers don't have XHR xhr = null } return xhr } function checkTypeSupport (type) { var xhr = getXHR() if (!xhr) return false try { xhr.responseType = type return xhr.responseType === type } catch (e) {} return false } // For some strange reason, Safari 7.0 reports typeof global.ArrayBuffer === 'object'. // Safari 7.1 appears to have fixed this bug. var haveArrayBuffer = typeof global.ArrayBuffer !== 'undefined' var haveSlice = haveArrayBuffer && isFunction(global.ArrayBuffer.prototype.slice) // If fetch is supported, then arraybuffer will be supported too. Skip calling // checkTypeSupport(), since that calls getXHR(). exports.arraybuffer = exports.fetch || (haveArrayBuffer && checkTypeSupport('arraybuffer')) // These next two tests unavoidably show warnings in Chrome. Since fetch will always // be used if it's available, just return false for these to avoid the warnings. exports.msstream = !exports.fetch && haveSlice && checkTypeSupport('ms-stream') exports.mozchunkedarraybuffer = !exports.fetch && haveArrayBuffer && checkTypeSupport('moz-chunked-arraybuffer') // If fetch is supported, then overrideMimeType will be supported too. Skip calling // getXHR(). exports.overrideMimeType = exports.fetch || (getXHR() ? isFunction(getXHR().overrideMimeType) : false) exports.vbArray = isFunction(global.VBArray) function isFunction (value) { return typeof value === 'function' } xhr = null // Help gc }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],222:[function(require,module,exports){ (function (process,global,Buffer){ var capability = require('./capability') var inherits = require('inherits') var response = require('./response') var stream = require('readable-stream') var toArrayBuffer = require('to-arraybuffer') var IncomingMessage = response.IncomingMessage var rStates = response.readyStates function decideMode (preferBinary, useFetch) { if (capability.fetch && useFetch) { return 'fetch' } else if (capability.mozchunkedarraybuffer) { return 'moz-chunked-arraybuffer' } else if (capability.msstream) { return 'ms-stream' } else if (capability.arraybuffer && preferBinary) { return 'arraybuffer' } else if (capability.vbArray && preferBinary) { return 'text:vbarray' } else { return 'text' } } var ClientRequest = module.exports = function (opts) { var self = this stream.Writable.call(self) self._opts = opts self._body = [] self._headers = {} if (opts.auth) self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64')) Object.keys(opts.headers).forEach(function (name) { self.setHeader(name, opts.headers[name]) }) var preferBinary var useFetch = true if (opts.mode === 'disable-fetch' || 'timeout' in opts) { // If the use of XHR should be preferred and includes preserving the 'content-type' header. // Force XHR to be used since the Fetch API does not yet support timeouts. useFetch = false preferBinary = true } else if (opts.mode === 'prefer-streaming') { // If streaming is a high priority but binary compatibility and // the accuracy of the 'content-type' header aren't preferBinary = false } else if (opts.mode === 'allow-wrong-content-type') { // If streaming is more important than preserving the 'content-type' header preferBinary = !capability.overrideMimeType } else if (!opts.mode || opts.mode === 'default' || opts.mode === 'prefer-fast') { // Use binary if text streaming may corrupt data or the content-type header, or for speed preferBinary = true } else { throw new Error('Invalid value for opts.mode') } self._mode = decideMode(preferBinary, useFetch) self.on('finish', function () { self._onFinish() }) } inherits(ClientRequest, stream.Writable) ClientRequest.prototype.setHeader = function (name, value) { var self = this var lowerName = name.toLowerCase() // This check is not necessary, but it prevents warnings from browsers about setting unsafe // headers. To be honest I'm not entirely sure hiding these warnings is a good thing, but // http-browserify did it, so I will too. if (unsafeHeaders.indexOf(lowerName) !== -1) return self._headers[lowerName] = { name: name, value: value } } ClientRequest.prototype.getHeader = function (name) { var header = this._headers[name.toLowerCase()] if (header) return header.value return null } ClientRequest.prototype.removeHeader = function (name) { var self = this delete self._headers[name.toLowerCase()] } ClientRequest.prototype._onFinish = function () { var self = this if (self._destroyed) return var opts = self._opts var headersObj = self._headers var body = null if (opts.method !== 'GET' && opts.method !== 'HEAD') { if (capability.blobConstructor) { body = new global.Blob(self._body.map(function (buffer) { return toArrayBuffer(buffer) }), { type: (headersObj['content-type'] || {}).value || '' }) } else { // get utf8 string body = Buffer.concat(self._body).toString() } } // create flattened list of headers var headersList = [] Object.keys(headersObj).forEach(function (keyName) { var name = headersObj[keyName].name var value = headersObj[keyName].value if (Array.isArray(value)) { value.forEach(function (v) { headersList.push([name, v]) }) } else { headersList.push([name, value]) } }) if (self._mode === 'fetch') { global.fetch(self._opts.url, { method: self._opts.method, headers: headersList, body: body || undefined, mode: 'cors', credentials: opts.withCredentials ? 'include' : 'same-origin' }).then(function (response) { self._fetchResponse = response self._connect() }, function (reason) { self.emit('error', reason) }) } else { var xhr = self._xhr = new global.XMLHttpRequest() try { xhr.open(self._opts.method, self._opts.url, true) } catch (err) { process.nextTick(function () { self.emit('error', err) }) return } // Can't set responseType on really old browsers if ('responseType' in xhr) xhr.responseType = self._mode.split(':')[0] if ('withCredentials' in xhr) xhr.withCredentials = !!opts.withCredentials if (self._mode === 'text' && 'overrideMimeType' in xhr) xhr.overrideMimeType('text/plain; charset=x-user-defined') if ('timeout' in opts) { xhr.timeout = opts.timeout xhr.ontimeout = function () { self.emit('timeout') } } headersList.forEach(function (header) { xhr.setRequestHeader(header[0], header[1]) }) self._response = null xhr.onreadystatechange = function () { switch (xhr.readyState) { case rStates.LOADING: case rStates.DONE: self._onXHRProgress() break } } // Necessary for streaming in Firefox, since xhr.response is ONLY defined // in onprogress, not in onreadystatechange with xhr.readyState = 3 if (self._mode === 'moz-chunked-arraybuffer') { xhr.onprogress = function () { self._onXHRProgress() } } xhr.onerror = function () { if (self._destroyed) return self.emit('error', new Error('XHR error')) } try { xhr.send(body) } catch (err) { process.nextTick(function () { self.emit('error', err) }) return } } } /** * Checks if xhr.status is readable and non-zero, indicating no error. * Even though the spec says it should be available in readyState 3, * accessing it throws an exception in IE8 */ function statusValid (xhr) { try { var status = xhr.status return (status !== null && status !== 0) } catch (e) { return false } } ClientRequest.prototype._onXHRProgress = function () { var self = this if (!statusValid(self._xhr) || self._destroyed) return if (!self._response) self._connect() self._response._onXHRProgress() } ClientRequest.prototype._connect = function () { var self = this if (self._destroyed) return self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode) self._response.on('error', function(err) { self.emit('error', err) }) self.emit('response', self._response) } ClientRequest.prototype._write = function (chunk, encoding, cb) { var self = this self._body.push(chunk) cb() } ClientRequest.prototype.abort = ClientRequest.prototype.destroy = function () { var self = this self._destroyed = true if (self._response) self._response._destroyed = true if (self._xhr) self._xhr.abort() // Currently, there isn't a way to truly abort a fetch. // If you like bikeshedding, see https://github.com/whatwg/fetch/issues/27 } ClientRequest.prototype.end = function (data, encoding, cb) { var self = this if (typeof data === 'function') { cb = data data = undefined } stream.Writable.prototype.end.call(self, data, encoding, cb) } ClientRequest.prototype.flushHeaders = function () {} ClientRequest.prototype.setTimeout = function () {} ClientRequest.prototype.setNoDelay = function () {} ClientRequest.prototype.setSocketKeepAlive = function () {} // Taken from http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader%28%29-method var unsafeHeaders = [ 'accept-charset', 'accept-encoding', 'access-control-request-headers', 'access-control-request-method', 'connection', 'content-length', 'cookie', 'cookie2', 'date', 'dnt', 'expect', 'host', 'keep-alive', 'origin', 'referer', 'te', 'trailer', 'transfer-encoding', 'upgrade', 'user-agent', 'via' ] }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) },{"./capability":221,"./response":223,"_process":200,"buffer":178,"inherits":185,"readable-stream":215,"to-arraybuffer":225}],223:[function(require,module,exports){ (function (process,global,Buffer){ var capability = require('./capability') var inherits = require('inherits') var stream = require('readable-stream') var rStates = exports.readyStates = { UNSENT: 0, OPENED: 1, HEADERS_RECEIVED: 2, LOADING: 3, DONE: 4 } var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode) { var self = this stream.Readable.call(self) self._mode = mode self.headers = {} self.rawHeaders = [] self.trailers = {} self.rawTrailers = [] // Fake the 'close' event, but only once 'end' fires self.on('end', function () { // The nextTick is necessary to prevent the 'request' module from causing an infinite loop process.nextTick(function () { self.emit('close') }) }) if (mode === 'fetch') { self._fetchResponse = response self.url = response.url self.statusCode = response.status self.statusMessage = response.statusText response.headers.forEach(function(header, key){ self.headers[key.toLowerCase()] = header self.rawHeaders.push(key, header) }) // TODO: this doesn't respect backpressure. Once WritableStream is available, this can be fixed var reader = response.body.getReader() function read () { reader.read().then(function (result) { if (self._destroyed) return if (result.done) { self.push(null) return } self.push(new Buffer(result.value)) read() }).catch(function(err) { self.emit('error', err) }) } read() } else { self._xhr = xhr self._pos = 0 self.url = xhr.responseURL self.statusCode = xhr.status self.statusMessage = xhr.statusText var headers = xhr.getAllResponseHeaders().split(/\r?\n/) headers.forEach(function (header) { var matches = header.match(/^([^:]+):\s*(.*)/) if (matches) { var key = matches[1].toLowerCase() if (key === 'set-cookie') { if (self.headers[key] === undefined) { self.headers[key] = [] } self.headers[key].push(matches[2]) } else if (self.headers[key] !== undefined) { self.headers[key] += ', ' + matches[2] } else { self.headers[key] = matches[2] } self.rawHeaders.push(matches[1], matches[2]) } }) self._charset = 'x-user-defined' if (!capability.overrideMimeType) { var mimeType = self.rawHeaders['mime-type'] if (mimeType) { var charsetMatch = mimeType.match(/;\s*charset=([^;])(;|$)/) if (charsetMatch) { self._charset = charsetMatch[1].toLowerCase() } } if (!self._charset) self._charset = 'utf-8' // best guess } } } inherits(IncomingMessage, stream.Readable) IncomingMessage.prototype._read = function () {} IncomingMessage.prototype._onXHRProgress = function () { var self = this var xhr = self._xhr var response = null switch (self._mode) { case 'text:vbarray': // For IE9 if (xhr.readyState !== rStates.DONE) break try { // This fails in IE8 response = new global.VBArray(xhr.responseBody).toArray() } catch (e) {} if (response !== null) { self.push(new Buffer(response)) break } // Falls through in IE8 case 'text': try { // This will fail when readyState = 3 in IE9. Switch mode and wait for readyState = 4 response = xhr.responseText } catch (e) { self._mode = 'text:vbarray' break } if (response.length > self._pos) { var newData = response.substr(self._pos) if (self._charset === 'x-user-defined') { var buffer = new Buffer(newData.length) for (var i = 0; i < newData.length; i++) buffer[i] = newData.charCodeAt(i) & 0xff self.push(buffer) } else { self.push(newData, self._charset) } self._pos = response.length } break case 'arraybuffer': if (xhr.readyState !== rStates.DONE || !xhr.response) break response = xhr.response self.push(new Buffer(new Uint8Array(response))) break case 'moz-chunked-arraybuffer': // take whole response = xhr.response if (xhr.readyState !== rStates.LOADING || !response) break self.push(new Buffer(new Uint8Array(response))) break case 'ms-stream': response = xhr.response if (xhr.readyState !== rStates.LOADING) break var reader = new global.MSStreamReader() reader.onprogress = function () { if (reader.result.byteLength > self._pos) { self.push(new Buffer(new Uint8Array(reader.result.slice(self._pos)))) self._pos = reader.result.byteLength } } reader.onload = function () { self.push(null) } // reader.onerror = ??? // TODO: this reader.readAsArrayBuffer(response) break } // The ms-stream case handles end separately in reader.onload() if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') { self.push(null) } } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) },{"./capability":221,"_process":200,"buffer":178,"inherits":185,"readable-stream":215}],224:[function(require,module,exports){ 'use strict'; var Buffer = require('safe-buffer').Buffer; var isEncoding = Buffer.isEncoding || function (encoding) { encoding = '' + encoding; switch (encoding && encoding.toLowerCase()) { case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': return true; default: return false; } }; function _normalizeEncoding(enc) { if (!enc) return 'utf8'; var retried; while (true) { switch (enc) { case 'utf8': case 'utf-8': return 'utf8'; case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return 'utf16le'; case 'latin1': case 'binary': return 'latin1'; case 'base64': case 'ascii': case 'hex': return enc; default: if (retried) return; // undefined enc = ('' + enc).toLowerCase(); retried = true; } } }; // Do not cache `Buffer.isEncoding` when checking encoding names as some // modules monkey-patch it to support additional encodings function normalizeEncoding(enc) { var nenc = _normalizeEncoding(enc); if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); return nenc || enc; } // StringDecoder provides an interface for efficiently splitting a series of // buffers into a series of JS strings without breaking apart multi-byte // characters. exports.StringDecoder = StringDecoder; function StringDecoder(encoding) { this.encoding = normalizeEncoding(encoding); var nb; switch (this.encoding) { case 'utf16le': this.text = utf16Text; this.end = utf16End; nb = 4; break; case 'utf8': this.fillLast = utf8FillLast; nb = 4; break; case 'base64': this.text = base64Text; this.end = base64End; nb = 3; break; default: this.write = simpleWrite; this.end = simpleEnd; return; } this.lastNeed = 0; this.lastTotal = 0; this.lastChar = Buffer.allocUnsafe(nb); } StringDecoder.prototype.write = function (buf) { if (buf.length === 0) return ''; var r; var i; if (this.lastNeed) { r = this.fillLast(buf); if (r === undefined) return ''; i = this.lastNeed; this.lastNeed = 0; } else { i = 0; } if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); return r || ''; }; StringDecoder.prototype.end = utf8End; // Returns only complete characters in a Buffer StringDecoder.prototype.text = utf8Text; // Attempts to complete a partial non-UTF-8 character using bytes from a Buffer StringDecoder.prototype.fillLast = function (buf) { if (this.lastNeed <= buf.length) { buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); return this.lastChar.toString(this.encoding, 0, this.lastTotal); } buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); this.lastNeed -= buf.length; }; // Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a // continuation byte. function utf8CheckByte(byte) { if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; return -1; } // Checks at most 3 bytes at the end of a Buffer in order to detect an // incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) // needed to complete the UTF-8 character (if applicable) are returned. function utf8CheckIncomplete(self, buf, i) { var j = buf.length - 1; if (j < i) return 0; var nb = utf8CheckByte(buf[j]); if (nb >= 0) { if (nb > 0) self.lastNeed = nb - 1; return nb; } if (--j < i) return 0; nb = utf8CheckByte(buf[j]); if (nb >= 0) { if (nb > 0) self.lastNeed = nb - 2; return nb; } if (--j < i) return 0; nb = utf8CheckByte(buf[j]); if (nb >= 0) { if (nb > 0) { if (nb === 2) nb = 0;else self.lastNeed = nb - 3; } return nb; } return 0; } // Validates as many continuation bytes for a multi-byte UTF-8 character as // needed or are available. If we see a non-continuation byte where we expect // one, we "replace" the validated continuation bytes we've seen so far with // UTF-8 replacement characters ('\ufffd'), to match v8's UTF-8 decoding // behavior. The continuation byte check is included three times in the case // where all of the continuation bytes for a character exist in the same buffer. // It is also done this way as a slight performance increase instead of using a // loop. function utf8CheckExtraBytes(self, buf, p) { if ((buf[0] & 0xC0) !== 0x80) { self.lastNeed = 0; return '\ufffd'.repeat(p); } if (self.lastNeed > 1 && buf.length > 1) { if ((buf[1] & 0xC0) !== 0x80) { self.lastNeed = 1; return '\ufffd'.repeat(p + 1); } if (self.lastNeed > 2 && buf.length > 2) { if ((buf[2] & 0xC0) !== 0x80) { self.lastNeed = 2; return '\ufffd'.repeat(p + 2); } } } } // Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. function utf8FillLast(buf) { var p = this.lastTotal - this.lastNeed; var r = utf8CheckExtraBytes(this, buf, p); if (r !== undefined) return r; if (this.lastNeed <= buf.length) { buf.copy(this.lastChar, p, 0, this.lastNeed); return this.lastChar.toString(this.encoding, 0, this.lastTotal); } buf.copy(this.lastChar, p, 0, buf.length); this.lastNeed -= buf.length; } // Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a // partial character, the character's bytes are buffered until the required // number of bytes are available. function utf8Text(buf, i) { var total = utf8CheckIncomplete(this, buf, i); if (!this.lastNeed) return buf.toString('utf8', i); this.lastTotal = total; var end = buf.length - (total - this.lastNeed); buf.copy(this.lastChar, 0, end); return buf.toString('utf8', i, end); } // For UTF-8, a replacement character for each buffered byte of a (partial) // character needs to be added to the output. function utf8End(buf) { var r = buf && buf.length ? this.write(buf) : ''; if (this.lastNeed) return r + '\ufffd'.repeat(this.lastTotal - this.lastNeed); return r; } // UTF-16LE typically needs two bytes per character, but even if we have an even // number of bytes available, we need to check if we end on a leading/high // surrogate. In that case, we need to wait for the next two bytes in order to // decode the last character properly. function utf16Text(buf, i) { if ((buf.length - i) % 2 === 0) { var r = buf.toString('utf16le', i); if (r) { var c = r.charCodeAt(r.length - 1); if (c >= 0xD800 && c <= 0xDBFF) { this.lastNeed = 2; this.lastTotal = 4; this.lastChar[0] = buf[buf.length - 2]; this.lastChar[1] = buf[buf.length - 1]; return r.slice(0, -1); } } return r; } this.lastNeed = 1; this.lastTotal = 2; this.lastChar[0] = buf[buf.length - 1]; return buf.toString('utf16le', i, buf.length - 1); } // For UTF-16LE we do not explicitly append special replacement characters if we // end on a partial character, we simply let v8 handle that. function utf16End(buf) { var r = buf && buf.length ? this.write(buf) : ''; if (this.lastNeed) { var end = this.lastTotal - this.lastNeed; return r + this.lastChar.toString('utf16le', 0, end); } return r; } function base64Text(buf, i) { var n = (buf.length - i) % 3; if (n === 0) return buf.toString('base64', i); this.lastNeed = 3 - n; this.lastTotal = 3; if (n === 1) { this.lastChar[0] = buf[buf.length - 1]; } else { this.lastChar[0] = buf[buf.length - 2]; this.lastChar[1] = buf[buf.length - 1]; } return buf.toString('base64', i, buf.length - n); } function base64End(buf) { var r = buf && buf.length ? this.write(buf) : ''; if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); return r; } // Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) function simpleWrite(buf) { return buf.toString(this.encoding); } function simpleEnd(buf) { return buf && buf.length ? this.write(buf) : ''; } },{"safe-buffer":218}],225:[function(require,module,exports){ var Buffer = require('buffer').Buffer module.exports = function (buf) { // If the buffer is backed by a Uint8Array, a faster version will work if (buf instanceof Uint8Array) { // If the buffer isn't a subarray, return the underlying ArrayBuffer if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) { return buf.buffer } else if (typeof buf.buffer.slice === 'function') { // Otherwise we need to get a proper copy return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) } } if (Buffer.isBuffer(buf)) { // This is the slow version that will work with any Buffer // implementation (even in old browsers) var arrayCopy = new Uint8Array(buf.length) var len = buf.length for (var i = 0; i < len; i++) { arrayCopy[i] = buf[i] } return arrayCopy.buffer } else { throw new Error('Argument must be a Buffer') } } },{"buffer":178}],226:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; var punycode = require('punycode'); var util = require('./util'); exports.parse = urlParse; exports.resolve = urlResolve; exports.resolveObject = urlResolveObject; exports.format = urlFormat; exports.Url = Url; function Url() { this.protocol = null; this.slashes = null; this.auth = null; this.host = null; this.port = null; this.hostname = null; this.hash = null; this.search = null; this.query = null; this.pathname = null; this.path = null; this.href = null; } // Reference: RFC 3986, RFC 1808, RFC 2396 // define these here so at least they only have to be // compiled once on the first module load. var protocolPattern = /^([a-z0-9.+-]+:)/i, portPattern = /:[0-9]*$/, // Special case for a simple path URL simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/, // RFC 2396: characters reserved for delimiting URLs. // We actually just auto-escape these. delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], // RFC 2396: characters not allowed for various reasons. unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims), // Allowed by RFCs, but cause of XSS attacks. Always escape these. autoEscape = ['\''].concat(unwise), // Characters that are never ever allowed in a hostname. // Note that any invalid chars are also handled, but these // are the ones that are *expected* to be seen, so we fast-path // them. nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape), hostEndingChars = ['/', '?', '#'], hostnameMaxLen = 255, hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/, hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, // protocols that can allow "unsafe" and "unwise" chars. unsafeProtocol = { 'javascript': true, 'javascript:': true }, // protocols that never have a hostname. hostlessProtocol = { 'javascript': true, 'javascript:': true }, // protocols that always contain a // bit. slashedProtocol = { 'http': true, 'https': true, 'ftp': true, 'gopher': true, 'file': true, 'http:': true, 'https:': true, 'ftp:': true, 'gopher:': true, 'file:': true }, querystring = require('querystring'); function urlParse(url, parseQueryString, slashesDenoteHost) { if (url && util.isObject(url) && url instanceof Url) return url; var u = new Url; u.parse(url, parseQueryString, slashesDenoteHost); return u; } Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { if (!util.isString(url)) { throw new TypeError("Parameter 'url' must be a string, not " + typeof url); } // Copy chrome, IE, opera backslash-handling behavior. // Back slashes before the query string get converted to forward slashes // See: https://code.google.com/p/chromium/issues/detail?id=25916 var queryIndex = url.indexOf('?'), splitter = (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#', uSplit = url.split(splitter), slashRegex = /\\/g; uSplit[0] = uSplit[0].replace(slashRegex, '/'); url = uSplit.join(splitter); var rest = url; // trim before proceeding. // This is to support parse stuff like " http://foo.com \n" rest = rest.trim(); if (!slashesDenoteHost && url.split('#').length === 1) { // Try fast path regexp var simplePath = simplePathPattern.exec(rest); if (simplePath) { this.path = rest; this.href = rest; this.pathname = simplePath[1]; if (simplePath[2]) { this.search = simplePath[2]; if (parseQueryString) { this.query = querystring.parse(this.search.substr(1)); } else { this.query = this.search.substr(1); } } else if (parseQueryString) { this.search = ''; this.query = {}; } return this; } } var proto = protocolPattern.exec(rest); if (proto) { proto = proto[0]; var lowerProto = proto.toLowerCase(); this.protocol = lowerProto; rest = rest.substr(proto.length); } // figure out if it's got a host // user@server is *always* interpreted as a hostname, and url // resolution will treat //foo/bar as host=foo,path=bar because that's // how the browser resolves relative URLs. if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { var slashes = rest.substr(0, 2) === '//'; if (slashes && !(proto && hostlessProtocol[proto])) { rest = rest.substr(2); this.slashes = true; } } if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) { // there's a hostname. // the first instance of /, ?, ;, or # ends the host. // // If there is an @ in the hostname, then non-host chars *are* allowed // to the left of the last @ sign, unless some host-ending character // comes *before* the @-sign. // URLs are obnoxious. // // ex: // http://a@b@c/ => user:a@b host:c // http://a@b?@c => user:a host:c path:/?@c // v0.12 TODO(isaacs): This is not quite how Chrome does things. // Review our test case against browsers more comprehensively. // find the first instance of any hostEndingChars var hostEnd = -1; for (var i = 0; i < hostEndingChars.length; i++) { var hec = rest.indexOf(hostEndingChars[i]); if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) hostEnd = hec; } // at this point, either we have an explicit point where the // auth portion cannot go past, or the last @ char is the decider. var auth, atSign; if (hostEnd === -1) { // atSign can be anywhere. atSign = rest.lastIndexOf('@'); } else { // atSign must be in auth portion. // http://a@b/c@d => host:b auth:a path:/c@d atSign = rest.lastIndexOf('@', hostEnd); } // Now we have a portion which is definitely the auth. // Pull that off. if (atSign !== -1) { auth = rest.slice(0, atSign); rest = rest.slice(atSign + 1); this.auth = decodeURIComponent(auth); } // the host is the remaining to the left of the first non-host char hostEnd = -1; for (var i = 0; i < nonHostChars.length; i++) { var hec = rest.indexOf(nonHostChars[i]); if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) hostEnd = hec; } // if we still have not hit it, then the entire thing is a host. if (hostEnd === -1) hostEnd = rest.length; this.host = rest.slice(0, hostEnd); rest = rest.slice(hostEnd); // pull out port. this.parseHost(); // we've indicated that there is a hostname, // so even if it's empty, it has to be present. this.hostname = this.hostname || ''; // if hostname begins with [ and ends with ] // assume that it's an IPv6 address. var ipv6Hostname = this.hostname[0] === '[' && this.hostname[this.hostname.length - 1] === ']'; // validate a little. if (!ipv6Hostname) { var hostparts = this.hostname.split(/\./); for (var i = 0, l = hostparts.length; i < l; i++) { var part = hostparts[i]; if (!part) continue; if (!part.match(hostnamePartPattern)) { var newpart = ''; for (var j = 0, k = part.length; j < k; j++) { if (part.charCodeAt(j) > 127) { // we replace non-ASCII char with a temporary placeholder // we need this to make sure size of hostname is not // broken by replacing non-ASCII by nothing newpart += 'x'; } else { newpart += part[j]; } } // we test again with ASCII char only if (!newpart.match(hostnamePartPattern)) { var validParts = hostparts.slice(0, i); var notHost = hostparts.slice(i + 1); var bit = part.match(hostnamePartStart); if (bit) { validParts.push(bit[1]); notHost.unshift(bit[2]); } if (notHost.length) { rest = '/' + notHost.join('.') + rest; } this.hostname = validParts.join('.'); break; } } } } if (this.hostname.length > hostnameMaxLen) { this.hostname = ''; } else { // hostnames are always lower case. this.hostname = this.hostname.toLowerCase(); } if (!ipv6Hostname) { // IDNA Support: Returns a punycoded representation of "domain". // It only converts parts of the domain name that // have non-ASCII characters, i.e. it doesn't matter if // you call it with a domain that already is ASCII-only. this.hostname = punycode.toASCII(this.hostname); } var p = this.port ? ':' + this.port : ''; var h = this.hostname || ''; this.host = h + p; this.href += this.host; // strip [ and ] from the hostname // the host field still retains them, though if (ipv6Hostname) { this.hostname = this.hostname.substr(1, this.hostname.length - 2); if (rest[0] !== '/') { rest = '/' + rest; } } } // now rest is set to the post-host stuff. // chop off any delim chars. if (!unsafeProtocol[lowerProto]) { // First, make 100% sure that any "autoEscape" chars get // escaped, even if encodeURIComponent doesn't think they // need to be. for (var i = 0, l = autoEscape.length; i < l; i++) { var ae = autoEscape[i]; if (rest.indexOf(ae) === -1) continue; var esc = encodeURIComponent(ae); if (esc === ae) { esc = escape(ae); } rest = rest.split(ae).join(esc); } } // chop off from the tail first. var hash = rest.indexOf('#'); if (hash !== -1) { // got a fragment string. this.hash = rest.substr(hash); rest = rest.slice(0, hash); } var qm = rest.indexOf('?'); if (qm !== -1) { this.search = rest.substr(qm); this.query = rest.substr(qm + 1); if (parseQueryString) { this.query = querystring.parse(this.query); } rest = rest.slice(0, qm); } else if (parseQueryString) { // no query string, but parseQueryString still requested this.search = ''; this.query = {}; } if (rest) this.pathname = rest; if (slashedProtocol[lowerProto] && this.hostname && !this.pathname) { this.pathname = '/'; } //to support http.request if (this.pathname || this.search) { var p = this.pathname || ''; var s = this.search || ''; this.path = p + s; } // finally, reconstruct the href based on what has been validated. this.href = this.format(); return this; }; // format a parsed object into a url string function urlFormat(obj) { // ensure it's an object, and not a string url. // If it's an obj, this is a no-op. // this way, you can call url_format() on strings // to clean up potentially wonky urls. if (util.isString(obj)) obj = urlParse(obj); if (!(obj instanceof Url)) return Url.prototype.format.call(obj); return obj.format(); } Url.prototype.format = function() { var auth = this.auth || ''; if (auth) { auth = encodeURIComponent(auth); auth = auth.replace(/%3A/i, ':'); auth += '@'; } var protocol = this.protocol || '', pathname = this.pathname || '', hash = this.hash || '', host = false, query = ''; if (this.host) { host = auth + this.host; } else if (this.hostname) { host = auth + (this.hostname.indexOf(':') === -1 ? this.hostname : '[' + this.hostname + ']'); if (this.port) { host += ':' + this.port; } } if (this.query && util.isObject(this.query) && Object.keys(this.query).length) { query = querystring.stringify(this.query); } var search = this.search || (query && ('?' + query)) || ''; if (protocol && protocol.substr(-1) !== ':') protocol += ':'; // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. // unless they had them to begin with. if (this.slashes || (!protocol || slashedProtocol[protocol]) && host !== false) { host = '//' + (host || ''); if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname; } else if (!host) { host = ''; } if (hash && hash.charAt(0) !== '#') hash = '#' + hash; if (search && search.charAt(0) !== '?') search = '?' + search; pathname = pathname.replace(/[?#]/g, function(match) { return encodeURIComponent(match); }); search = search.replace('#', '%23'); return protocol + host + pathname + search + hash; }; function urlResolve(source, relative) { return urlParse(source, false, true).resolve(relative); } Url.prototype.resolve = function(relative) { return this.resolveObject(urlParse(relative, false, true)).format(); }; function urlResolveObject(source, relative) { if (!source) return relative; return urlParse(source, false, true).resolveObject(relative); } Url.prototype.resolveObject = function(relative) { if (util.isString(relative)) { var rel = new Url(); rel.parse(relative, false, true); relative = rel; } var result = new Url(); var tkeys = Object.keys(this); for (var tk = 0; tk < tkeys.length; tk++) { var tkey = tkeys[tk]; result[tkey] = this[tkey]; } // hash is always overridden, no matter what. // even href="" will remove it. result.hash = relative.hash; // if the relative url is empty, then there's nothing left to do here. if (relative.href === '') { result.href = result.format(); return result; } // hrefs like //foo/bar always cut to the protocol. if (relative.slashes && !relative.protocol) { // take everything except the protocol from relative var rkeys = Object.keys(relative); for (var rk = 0; rk < rkeys.length; rk++) { var rkey = rkeys[rk]; if (rkey !== 'protocol') result[rkey] = relative[rkey]; } //urlParse appends trailing / to urls like http://www.example.com if (slashedProtocol[result.protocol] && result.hostname && !result.pathname) { result.path = result.pathname = '/'; } result.href = result.format(); return result; } if (relative.protocol && relative.protocol !== result.protocol) { // if it's a known url protocol, then changing // the protocol does weird things // first, if it's not file:, then we MUST have a host, // and if there was a path // to begin with, then we MUST have a path. // if it is file:, then the host is dropped, // because that's known to be hostless. // anything else is assumed to be absolute. if (!slashedProtocol[relative.protocol]) { var keys = Object.keys(relative); for (var v = 0; v < keys.length; v++) { var k = keys[v]; result[k] = relative[k]; } result.href = result.format(); return result; } result.protocol = relative.protocol; if (!relative.host && !hostlessProtocol[relative.protocol]) { var relPath = (relative.pathname || '').split('/'); while (relPath.length && !(relative.host = relPath.shift())); if (!relative.host) relative.host = ''; if (!relative.hostname) relative.hostname = ''; if (relPath[0] !== '') relPath.unshift(''); if (relPath.length < 2) relPath.unshift(''); result.pathname = relPath.join('/'); } else { result.pathname = relative.pathname; } result.search = relative.search; result.query = relative.query; result.host = relative.host || ''; result.auth = relative.auth; result.hostname = relative.hostname || relative.host; result.port = relative.port; // to support http.request if (result.pathname || result.search) { var p = result.pathname || ''; var s = result.search || ''; result.path = p + s; } result.slashes = result.slashes || relative.slashes; result.href = result.format(); return result; } var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'), isRelAbs = ( relative.host || relative.pathname && relative.pathname.charAt(0) === '/' ), mustEndAbs = (isRelAbs || isSourceAbs || (result.host && relative.pathname)), removeAllDots = mustEndAbs, srcPath = result.pathname && result.pathname.split('/') || [], relPath = relative.pathname && relative.pathname.split('/') || [], psychotic = result.protocol && !slashedProtocol[result.protocol]; // if the url is a non-slashed url, then relative // links like ../.. should be able // to crawl up to the hostname, as well. This is strange. // result.protocol has already been set by now. // Later on, put the first path part into the host field. if (psychotic) { result.hostname = ''; result.port = null; if (result.host) { if (srcPath[0] === '') srcPath[0] = result.host; else srcPath.unshift(result.host); } result.host = ''; if (relative.protocol) { relative.hostname = null; relative.port = null; if (relative.host) { if (relPath[0] === '') relPath[0] = relative.host; else relPath.unshift(relative.host); } relative.host = null; } mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); } if (isRelAbs) { // it's absolute. result.host = (relative.host || relative.host === '') ? relative.host : result.host; result.hostname = (relative.hostname || relative.hostname === '') ? relative.hostname : result.hostname; result.search = relative.search; result.query = relative.query; srcPath = relPath; // fall through to the dot-handling below. } else if (relPath.length) { // it's relative // throw away the existing file, and take the new path instead. if (!srcPath) srcPath = []; srcPath.pop(); srcPath = srcPath.concat(relPath); result.search = relative.search; result.query = relative.query; } else if (!util.isNullOrUndefined(relative.search)) { // just pull out the search. // like href='?foo'. // Put this after the other two cases because it simplifies the booleans if (psychotic) { result.hostname = result.host = srcPath.shift(); //occationaly the auth can get stuck only in host //this especially happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') var authInHost = result.host && result.host.indexOf('@') > 0 ? result.host.split('@') : false; if (authInHost) { result.auth = authInHost.shift(); result.host = result.hostname = authInHost.shift(); } } result.search = relative.search; result.query = relative.query; //to support http.request if (!util.isNull(result.pathname) || !util.isNull(result.search)) { result.path = (result.pathname ? result.pathname : '') + (result.search ? result.search : ''); } result.href = result.format(); return result; } if (!srcPath.length) { // no path at all. easy. // we've already handled the other stuff above. result.pathname = null; //to support http.request if (result.search) { result.path = '/' + result.search; } else { result.path = null; } result.href = result.format(); return result; } // if a url ENDs in . or .., then it must get a trailing slash. // however, if it ends in anything else non-slashy, // then it must NOT get a trailing slash. var last = srcPath.slice(-1)[0]; var hasTrailingSlash = ( (result.host || relative.host || srcPath.length > 1) && (last === '.' || last === '..') || last === ''); // strip single dots, resolve double dots to parent dir // if the path tries to go above the root, `up` ends up > 0 var up = 0; for (var i = srcPath.length; i >= 0; i--) { last = srcPath[i]; if (last === '.') { srcPath.splice(i, 1); } else if (last === '..') { srcPath.splice(i, 1); up++; } else if (up) { srcPath.splice(i, 1); up--; } } // if the path is allowed to go above the root, restore leading ..s if (!mustEndAbs && !removeAllDots) { for (; up--; up) { srcPath.unshift('..'); } } if (mustEndAbs && srcPath[0] !== '' && (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { srcPath.unshift(''); } if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { srcPath.push(''); } var isAbsolute = srcPath[0] === '' || (srcPath[0] && srcPath[0].charAt(0) === '/'); // put the host back if (psychotic) { result.hostname = result.host = isAbsolute ? '' : srcPath.length ? srcPath.shift() : ''; //occationaly the auth can get stuck only in host //this especially happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') var authInHost = result.host && result.host.indexOf('@') > 0 ? result.host.split('@') : false; if (authInHost) { result.auth = authInHost.shift(); result.host = result.hostname = authInHost.shift(); } } mustEndAbs = mustEndAbs || (result.host && srcPath.length); if (mustEndAbs && !isAbsolute) { srcPath.unshift(''); } if (!srcPath.length) { result.pathname = null; result.path = null; } else { result.pathname = srcPath.join('/'); } //to support request.http if (!util.isNull(result.pathname) || !util.isNull(result.search)) { result.path = (result.pathname ? result.pathname : '') + (result.search ? result.search : ''); } result.auth = relative.auth || result.auth; result.slashes = result.slashes || relative.slashes; result.href = result.format(); return result; }; Url.prototype.parseHost = function() { var host = this.host; var port = portPattern.exec(host); if (port) { port = port[0]; if (port !== ':') { this.port = port.substr(1); } host = host.substr(0, host.length - port.length); } if (host) this.hostname = host; }; },{"./util":227,"punycode":201,"querystring":204}],227:[function(require,module,exports){ 'use strict'; module.exports = { isString: function(arg) { return typeof(arg) === 'string'; }, isObject: function(arg) { return typeof(arg) === 'object' && arg !== null; }, isNull: function(arg) { return arg === null; }, isNullOrUndefined: function(arg) { return arg == null; } }; },{}],228:[function(require,module,exports){ (function (global){ /** * Module exports. */ module.exports = deprecate; /** * Mark that a method should not be used. * Returns a modified function which warns once by default. * * If `localStorage.noDeprecation = true` is set, then it is a no-op. * * If `localStorage.throwDeprecation = true` is set, then deprecated functions * will throw an Error when invoked. * * If `localStorage.traceDeprecation = true` is set, then deprecated functions * will invoke `console.trace()` instead of `console.error()`. * * @param {Function} fn - the function to deprecate * @param {String} msg - the string to print to the console when `fn` is invoked * @returns {Function} a new "deprecated" version of `fn` * @api public */ function deprecate (fn, msg) { if (config('noDeprecation')) { return fn; } var warned = false; function deprecated() { if (!warned) { if (config('throwDeprecation')) { throw new Error(msg); } else if (config('traceDeprecation')) { console.trace(msg); } else { console.warn(msg); } warned = true; } return fn.apply(this, arguments); } return deprecated; } /** * Checks `localStorage` for boolean values for the given `name`. * * @param {String} name * @returns {Boolean} * @api private */ function config (name) { // accessing global.localStorage can trigger a DOMException in sandboxed iframes try { if (!global.localStorage) return false; } catch (_) { return false; } var val = global.localStorage[name]; if (null == val) return false; return String(val).toLowerCase() === 'true'; } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],229:[function(require,module,exports){ arguments[4][185][0].apply(exports,arguments) },{"dup":185}],230:[function(require,module,exports){ module.exports = function isBuffer(arg) { return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function'; } },{}],231:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. var formatRegExp = /%[sdj%]/g; exports.format = function(f) { if (!isString(f)) { var objects = []; for (var i = 0; i < arguments.length; i++) { objects.push(inspect(arguments[i])); } return objects.join(' '); } var i = 1; var args = arguments; var len = args.length; var str = String(f).replace(formatRegExp, function(x) { if (x === '%%') return '%'; if (i >= len) return x; switch (x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]); case '%j': try { return JSON.stringify(args[i++]); } catch (_) { return '[Circular]'; } default: return x; } }); for (var x = args[i]; i < len; x = args[++i]) { if (isNull(x) || !isObject(x)) { str += ' ' + x; } else { str += ' ' + inspect(x); } } return str; }; // Mark that a method should not be used. // Returns a modified function which warns once by default. // If --no-deprecation is set, then it is a no-op. exports.deprecate = function(fn, msg) { // Allow for deprecating things in the process of starting up. if (isUndefined(global.process)) { return function() { return exports.deprecate(fn, msg).apply(this, arguments); }; } if (process.noDeprecation === true) { return fn; } var warned = false; function deprecated() { if (!warned) { if (process.throwDeprecation) { throw new Error(msg); } else if (process.traceDeprecation) { console.trace(msg); } else { console.error(msg); } warned = true; } return fn.apply(this, arguments); } return deprecated; }; var debugs = {}; var debugEnviron; exports.debuglog = function(set) { if (isUndefined(debugEnviron)) debugEnviron = process.env.NODE_DEBUG || ''; set = set.toUpperCase(); if (!debugs[set]) { if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { var pid = process.pid; debugs[set] = function() { var msg = exports.format.apply(exports, arguments); console.error('%s %d: %s', set, pid, msg); }; } else { debugs[set] = function() {}; } } return debugs[set]; }; /** * Echos the value of a value. Trys to print the value out * in the best way possible given the different types. * * @param {Object} obj The object to print out. * @param {Object} opts Optional options object that alters the output. */ /* legacy: obj, showHidden, depth, colors*/ function inspect(obj, opts) { // default options var ctx = { seen: [], stylize: stylizeNoColor }; // legacy... if (arguments.length >= 3) ctx.depth = arguments[2]; if (arguments.length >= 4) ctx.colors = arguments[3]; if (isBoolean(opts)) { // legacy... ctx.showHidden = opts; } else if (opts) { // got an "options" object exports._extend(ctx, opts); } // set default options if (isUndefined(ctx.showHidden)) ctx.showHidden = false; if (isUndefined(ctx.depth)) ctx.depth = 2; if (isUndefined(ctx.colors)) ctx.colors = false; if (isUndefined(ctx.customInspect)) ctx.customInspect = true; if (ctx.colors) ctx.stylize = stylizeWithColor; return formatValue(ctx, obj, ctx.depth); } exports.inspect = inspect; // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics inspect.colors = { 'bold' : [1, 22], 'italic' : [3, 23], 'underline' : [4, 24], 'inverse' : [7, 27], 'white' : [37, 39], 'grey' : [90, 39], 'black' : [30, 39], 'blue' : [34, 39], 'cyan' : [36, 39], 'green' : [32, 39], 'magenta' : [35, 39], 'red' : [31, 39], 'yellow' : [33, 39] }; // Don't use 'blue' not visible on cmd.exe inspect.styles = { 'special': 'cyan', 'number': 'yellow', 'boolean': 'yellow', 'undefined': 'grey', 'null': 'bold', 'string': 'green', 'date': 'magenta', // "name": intentionally not styling 'regexp': 'red' }; function stylizeWithColor(str, styleType) { var style = inspect.styles[styleType]; if (style) { return '\u001b[' + inspect.colors[style][0] + 'm' + str + '\u001b[' + inspect.colors[style][1] + 'm'; } else { return str; } } function stylizeNoColor(str, styleType) { return str; } function arrayToHash(array) { var hash = {}; array.forEach(function(val, idx) { hash[val] = true; }); return hash; } function formatValue(ctx, value, recurseTimes) { // Provide a hook for user-specified inspect functions. // Check that value is an object with an inspect function on it if (ctx.customInspect && value && isFunction(value.inspect) && // Filter out the util module, it's inspect function is special value.inspect !== exports.inspect && // Also filter out any prototype objects using the circular check. !(value.constructor && value.constructor.prototype === value)) { var ret = value.inspect(recurseTimes, ctx); if (!isString(ret)) { ret = formatValue(ctx, ret, recurseTimes); } return ret; } // Primitive types cannot have properties var primitive = formatPrimitive(ctx, value); if (primitive) { return primitive; } // Look up the keys of the object. var keys = Object.keys(value); var visibleKeys = arrayToHash(keys); if (ctx.showHidden) { keys = Object.getOwnPropertyNames(value); } // IE doesn't make error fields non-enumerable // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx if (isError(value) && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { return formatError(value); } // Some type of object without properties can be shortcutted. if (keys.length === 0) { if (isFunction(value)) { var name = value.name ? ': ' + value.name : ''; return ctx.stylize('[Function' + name + ']', 'special'); } if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } if (isDate(value)) { return ctx.stylize(Date.prototype.toString.call(value), 'date'); } if (isError(value)) { return formatError(value); } } var base = '', array = false, braces = ['{', '}']; // Make Array say that they are Array if (isArray(value)) { array = true; braces = ['[', ']']; } // Make functions say that they are functions if (isFunction(value)) { var n = value.name ? ': ' + value.name : ''; base = ' [Function' + n + ']'; } // Make RegExps say that they are RegExps if (isRegExp(value)) { base = ' ' + RegExp.prototype.toString.call(value); } // Make dates with properties first say the date if (isDate(value)) { base = ' ' + Date.prototype.toUTCString.call(value); } // Make error with message first say the error if (isError(value)) { base = ' ' + formatError(value); } if (keys.length === 0 && (!array || value.length == 0)) { return braces[0] + base + braces[1]; } if (recurseTimes < 0) { if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } else { return ctx.stylize('[Object]', 'special'); } } ctx.seen.push(value); var output; if (array) { output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); } else { output = keys.map(function(key) { return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); }); } ctx.seen.pop(); return reduceToSingleString(output, base, braces); } function formatPrimitive(ctx, value) { if (isUndefined(value)) return ctx.stylize('undefined', 'undefined'); if (isString(value)) { var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') .replace(/'/g, "\\'") .replace(/\\"/g, '"') + '\''; return ctx.stylize(simple, 'string'); } if (isNumber(value)) return ctx.stylize('' + value, 'number'); if (isBoolean(value)) return ctx.stylize('' + value, 'boolean'); // For some reason typeof null is "object", so special case here. if (isNull(value)) return ctx.stylize('null', 'null'); } function formatError(value) { return '[' + Error.prototype.toString.call(value) + ']'; } function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { var output = []; for (var i = 0, l = value.length; i < l; ++i) { if (hasOwnProperty(value, String(i))) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true)); } else { output.push(''); } } keys.forEach(function(key) { if (!key.match(/^\d+$/)) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); } }); return output; } function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { var name, str, desc; desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; if (desc.get) { if (desc.set) { str = ctx.stylize('[Getter/Setter]', 'special'); } else { str = ctx.stylize('[Getter]', 'special'); } } else { if (desc.set) { str = ctx.stylize('[Setter]', 'special'); } } if (!hasOwnProperty(visibleKeys, key)) { name = '[' + key + ']'; } if (!str) { if (ctx.seen.indexOf(desc.value) < 0) { if (isNull(recurseTimes)) { str = formatValue(ctx, desc.value, null); } else { str = formatValue(ctx, desc.value, recurseTimes - 1); } if (str.indexOf('\n') > -1) { if (array) { str = str.split('\n').map(function(line) { return ' ' + line; }).join('\n').substr(2); } else { str = '\n' + str.split('\n').map(function(line) { return ' ' + line; }).join('\n'); } } } else { str = ctx.stylize('[Circular]', 'special'); } } if (isUndefined(name)) { if (array && key.match(/^\d+$/)) { return str; } name = JSON.stringify('' + key); if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { name = name.substr(1, name.length - 2); name = ctx.stylize(name, 'name'); } else { name = name.replace(/'/g, "\\'") .replace(/\\"/g, '"') .replace(/(^"|"$)/g, "'"); name = ctx.stylize(name, 'string'); } } return name + ': ' + str; } function reduceToSingleString(output, base, braces) { var numLinesEst = 0; var length = output.reduce(function(prev, cur) { numLinesEst++; if (cur.indexOf('\n') >= 0) numLinesEst++; return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; }, 0); if (length > 60) { return braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n ') + ' ' + braces[1]; } return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; } // NOTE: These type checking functions intentionally don't use `instanceof` // because it is fragile and can be easily faked with `Object.create()`. function isArray(ar) { return Array.isArray(ar); } exports.isArray = isArray; function isBoolean(arg) { return typeof arg === 'boolean'; } exports.isBoolean = isBoolean; function isNull(arg) { return arg === null; } exports.isNull = isNull; function isNullOrUndefined(arg) { return arg == null; } exports.isNullOrUndefined = isNullOrUndefined; function isNumber(arg) { return typeof arg === 'number'; } exports.isNumber = isNumber; function isString(arg) { return typeof arg === 'string'; } exports.isString = isString; function isSymbol(arg) { return typeof arg === 'symbol'; } exports.isSymbol = isSymbol; function isUndefined(arg) { return arg === void 0; } exports.isUndefined = isUndefined; function isRegExp(re) { return isObject(re) && objectToString(re) === '[object RegExp]'; } exports.isRegExp = isRegExp; function isObject(arg) { return typeof arg === 'object' && arg !== null; } exports.isObject = isObject; function isDate(d) { return isObject(d) && objectToString(d) === '[object Date]'; } exports.isDate = isDate; function isError(e) { return isObject(e) && (objectToString(e) === '[object Error]' || e instanceof Error); } exports.isError = isError; function isFunction(arg) { return typeof arg === 'function'; } exports.isFunction = isFunction; function isPrimitive(arg) { return arg === null || typeof arg === 'boolean' || typeof arg === 'number' || typeof arg === 'string' || typeof arg === 'symbol' || // ES6 symbol typeof arg === 'undefined'; } exports.isPrimitive = isPrimitive; exports.isBuffer = require('./support/isBuffer'); function objectToString(o) { return Object.prototype.toString.call(o); } function pad(n) { return n < 10 ? '0' + n.toString(10) : n.toString(10); } var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // 26 Feb 16:19:34 function timestamp() { var d = new Date(); var time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(':'); return [d.getDate(), months[d.getMonth()], time].join(' '); } // log is just a thin wrapper to console.log that prepends a timestamp exports.log = function() { console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); }; /** * Inherit the prototype methods from one constructor into another. * * The Function.prototype.inherits from lang.js rewritten as a standalone * function (not on Function.prototype). NOTE: If this file is to be loaded * during bootstrapping this function needs to be rewritten using some native * functions as prototype setup using normal JavaScript does not work as * expected during bootstrapping (see mirror.js in r114903). * * @param {function} ctor Constructor function which needs to inherit the * prototype. * @param {function} superCtor Constructor function to inherit prototype from. */ exports.inherits = require('inherits'); exports._extend = function(origin, add) { // Don't do anything if add isn't an object if (!add || !isObject(add)) return origin; var keys = Object.keys(add); var i = keys.length; while (i--) { origin[keys[i]] = add[keys[i]]; } return origin; }; function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./support/isBuffer":230,"_process":200,"inherits":229}],232:[function(require,module,exports){ module.exports = extend var hasOwnProperty = Object.prototype.hasOwnProperty; function extend() { var target = {} for (var i = 0; i < arguments.length; i++) { var source = arguments[i] for (var key in source) { if (hasOwnProperty.call(source, key)) { target[key] = source[key] } } } return target } },{}]},{},[1]);