UNPKG

ccxt-bybit

Version:

A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges

912 lines (880 loc) 40.5 kB
'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 = {}) { await this.loadMarkets (); const market = this.market (symbol); const request = { 'baseTokenAddress': market['baseId'], 'quoteTokenAddress': market['quoteId'], 'interval': this.timeframes[timeframe], }; if (since === undefined) { throw new ExchangeError (this.id + ' fetchOHLCV requires a since argument'); } 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, '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, }; 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 } } };