# -*- coding: utf-8 -*-

# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code

from ccxt.base.exchange import Exchange
import base64
import math
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadResponse
from ccxt.base.errors import DDoSProtection
from ccxt.base.decimal_to_precision import TRUNCATE
from ccxt.base.decimal_to_precision import DECIMAL_PLACES
from ccxt.base.decimal_to_precision import NO_PADDING


class bytetrade(Exchange):

    def describe(self):
        return self.deep_extend(super(bytetrade, self).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,
            },
        })

    def fetch_currencies(self, params={}):
        currencies = self.publicGetCurrencies(params)
        result = {}
        for i in range(0, len(currencies)):
            currency = currencies[i]
            id = self.safe_string(currency, 'code')
            code = None
            if id in self.commonCurrencies:
                code = self.commonCurrencies[id]
            else:
                code = self.safe_string(currency, 'name')
            name = self.safe_string(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"
            #       }
            #     }
            #   }
            #   ]
            active = self.safe_value(currency, 'active')
            limits = self.safe_value(currency, 'limits')
            deposit = self.safe_value(limits, 'deposit')
            amountPrecision = self.safe_integer(currency, 'basePrecision')
            maxDeposit = self.safe_float(deposit, 'max')
            if maxDeposit == -1.0:
                maxDeposit = None
            withdraw = self.safe_value(limits, 'withdraw')
            maxWithdraw = self.safe_float(withdraw, 'max')
            if maxWithdraw == -1.0:
                maxWithdraw = None
            result[code] = {
                'id': id,
                'code': code,
                'name': name,
                'active': active,
                'precision': {
                    'amount': amountPrecision,
                    'price': None,
                },
                'fee': None,
                'limits': {
                    'amount': {'min': None, 'max': None},
                    'price': {'min': None, 'max': None},
                    'cost': {'min': None, 'max': None},
                    'deposit': {
                        'min': self.safe_float(deposit, 'min'),
                        'max': maxDeposit,
                    },
                    'withdraw': {
                        'min': self.safe_float(withdraw, 'min'),
                        'max': maxWithdraw,
                    },
                },
                'info': currency,
            }
        return result

    def fetch_markets(self, params={}):
        markets = self.publicGetSymbols(params)
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'symbol')
            base = self.safe_string(market, 'baseName')
            quote = self.safe_string(market, 'quoteName')
            normalBase = base.split('@')[0]
            normalQuote = quote.split('@')[0]
            normalSymbol = normalBase + '/' + normalQuote
            baseId = self.safe_string(market, 'base')
            quoteId = self.safe_string(market, 'quote')
            if baseId in self.commonCurrencies:
                base = self.commonCurrencies[baseId]
            if quoteId in self.commonCurrencies:
                quote = self.commonCurrencies[quoteId]
            symbol = base + '/' + quote
            limits = self.safe_value(market, 'limits', {})
            amount = self.safe_value(limits, 'amount', {})
            price = self.safe_value(limits, 'price', {})
            precision = self.safe_value(market, 'precision', {})
            active = self.safe_string(market, 'active')
            entry = {
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'info': market,
                'active': active,
                'precision': {
                    'amount': self.safe_integer(precision, 'amount'),
                    'price': self.safe_integer(precision, 'price'),
                },
                'normalSymbol': normalSymbol,
                'limits': {
                    'amount': {
                        'min': self.safe_float(amount, 'min'),
                        'max': self.safe_float(amount, 'max'),
                    },
                    'price': {
                        'min': self.safe_float(price, 'min'),
                        'max': self.safe_float(price, 'max'),
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
            }
            result.append(entry)
        return result

    def fetch_balance(self, params={}):
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired(self.id + ' fetchDeposits requires self.apiKey or userid argument')
        self.load_markets()
        request = {
            'userid': self.apiKey,
        }
        balances = self.publicGetBalance(self.extend(request, params))
        result = {'info': balances}
        for i in range(0, len(balances)):
            balance = balances[i]
            currencyId = self.safe_string(balance, 'code')
            code = self.safe_currency_code(currencyId, None)
            account = self.account()
            account['free'] = self.safe_float(balance, 'free')
            account['used'] = self.safe_float(balance, 'used')
            result[code] = account
        return self.parse_balance(result)

    def fetch_order_book(self, symbol, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # default = maximum = 100
        response = self.marketGetDepth(self.extend(request, params))
        timestamp = self.safe_value(response, 'timestamp')
        orderbook = self.parse_order_book(response, timestamp)
        return orderbook

    def parse_ticker(self, ticker, market=None):
        timestamp = self.safe_integer(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"
        #         }
        #     ]
        #
        symbol = None
        marketId = self.safe_string(ticker, 'symbol')
        if marketId in self.markets_by_id:
            market = self.markets_by_id[marketId]
        else:
            baseId = self.safe_string(ticker, 'base')
            quoteId = self.safe_string(ticker, 'quote')
            if (baseId is not None) and (quoteId is not None):
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
        if (symbol is None) and (market is not None):
            symbol = market['symbol']
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_float(ticker, 'high'),
            'low': self.safe_float(ticker, 'low'),
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': self.safe_float(ticker, 'weightedAvgPrice'),
            'open': self.safe_float(ticker, 'open'),
            'close': self.safe_float(ticker, 'close'),
            'last': self.safe_float(ticker, 'last'),
            'previousClose': None,  # previous day close
            'change': self.safe_float(ticker, 'change'),
            'percentage': self.safe_float(ticker, 'percentage'),
            'average': None,
            'baseVolume': self.safe_float(ticker, 'baseVolume'),
            'quoteVolume': self.safe_float(ticker, 'quoteVolume'),
            'info': ticker,
        }

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = self.marketGetTickers(self.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 isinstance(response, list):
            ticker = self.safe_value(response, 0)
            if ticker is None:
                raise BadResponse(self.id + ' fetchTicker() returned an empty response')
            return self.parse_ticker(ticker, market)
        return self.parse_ticker(response, market)

    def parse_tickers(self, rawTickers, symbols=None):
        tickers = []
        for i in range(0, len(rawTickers)):
            tickers.append(self.parse_ticker(rawTickers[i]))
        return self.filter_by_array(tickers, 'symbol', symbols)

    def fetch_bids_asks(self, symbols=None, params={}):
        self.load_markets()
        rawTickers = self.marketGetDepth(params)
        return self.parse_tickers(rawTickers, symbols)

    def fetch_tickers(self, symbols=None, params={}):
        self.load_markets()
        rawTickers = self.marketGetTickers(params)
        return self.parse_tickers(rawTickers, symbols)

    def parse_ohlcv(self, ohlcv, market=None, timeframe='1m', since=None, limit=None):
        return [
            ohlcv[0],
            float(ohlcv[1]),
            float(ohlcv[2]),
            float(ohlcv[3]),
            float(ohlcv[4]),
            float(ohlcv[5]),
        ]

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'timeframe': self.timeframes[timeframe],
        }
        if since is not None:
            request['since'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.marketGetKlines(self.extend(request, params))
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    def parse_trade(self, trade, market=None):
        timestamp = self.safe_integer(trade, 'timestamp')
        price = self.safe_float(trade, 'price')
        amount = self.safe_float(trade, 'amount')
        cost = self.safe_float(trade, 'cost')
        id = self.safe_string(trade, 'id')
        type = self.safe_string(trade, 'type')
        takerOrMaker = self.safe_string(trade, 'takerOrMaker')
        side = self.safe_string(trade, 'side')
        datetime = self.safe_string(trade, 'datetime')
        order = self.safe_string(trade, 'order')
        fee = self.safe_value(trade, 'fee')
        symbol = None
        if market is None:
            marketId = self.safe_string(trade, 'symbol')
            market = self.safe_value(self.markets_by_id, marketId)
        if market is not None:
            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,
        }

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if since is not None:
            request['since'] = since
        if limit is not None:
            request['limit'] = limit  # default = 100, maximum = 500
        response = self.marketGetTrades(self.extend(request, params))
        return self.parse_trades(response, market, since, limit)

    def parse_order(self, order, market=None):
        status = self.safe_string(order, 'status')
        symbol = None
        marketId = self.safe_string(order, 'symbol')
        if marketId in self.markets_by_id:
            market = self.markets_by_id[marketId]
        else:
            baseId = self.safe_string(order, 'base')
            quoteId = self.safe_string(order, 'quote')
            if (baseId is not None) and (quoteId is not None):
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
        if (symbol is None) and (market is not None):
            symbol = market['symbol']
        timestamp = self.safe_integer(order, 'timestamp')
        datetime = self.safe_string(order, 'datetime')
        lastTradeTimestamp = self.safe_integer(order, 'lastTradeTimestamp')
        price = self.safe_float(order, 'price')
        amount = self.safe_float(order, 'amount')
        filled = self.safe_float(order, 'filled')
        remaining = self.safe_float(order, 'remaining')
        cost = self.safe_float(order, 'cost')
        average = self.safe_float(order, 'average')
        id = self.safe_string(order, 'id')
        type = self.safe_string(order, 'type')
        side = self.safe_string(order, 'side')
        fee = self.safe_value(order, 'fee')
        return {
            'info': order,
            'id': id,
            'clientOrderId': None,
            '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': None,
        }

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        self.check_required_dependencies()
        if self.apiKey is None:
            raise ArgumentsRequired('createOrder requires self.apiKey or userid in params')
        self.load_markets()
        market = self.market(symbol)
        sideNum = None
        typeNum = None
        if side == 'sell':
            sideNum = 1
        else:
            sideNum = 2
        if type == 'limit':
            typeNum = 1
        else:
            typeNum = 2
            price = 0
        normalSymbol = market['normalSymbol']
        baseId = market['baseId']
        baseCurrency = self.currency(market['base'])
        amountTruncated = self.amount_to_precision(symbol, amount)
        amountChain = self.to_wei(amountTruncated, baseCurrency['precision']['amount'])
        quoteId = market['quoteId']
        quoteCurrency = self.currency(market['quote'])
        priceRounded = self.price_to_precision(symbol, price)
        priceChain = self.to_wei(priceRounded, quoteCurrency['precision']['amount'])
        now = self.milliseconds()
        expiration = self.milliseconds()
        datetime = self.iso8601(now)
        datetime = datetime.split('.')[0]
        expirationDatetime = self.iso8601(expiration)
        expirationDatetime = expirationDatetime.split('.')[0]
        defaultDappId = 'Sagittarius'
        dappId = self.safe_string(params, 'dappId', defaultDappId)
        defaultFee = self.safe_string(self.options, 'fee', '300000000000000')
        fee = self.safe_string(params, 'fee', defaultFee)
        eightBytes = self.integer_pow('2', '64')
        allByteStringArray = [
            self.number_to_be(1, 32),
            self.number_to_le(int(math.floor(now / 1000)), 4),
            self.number_to_le(1, 1),
            self.number_to_le(int(math.floor(expiration / 1000)), 4),
            self.number_to_le(1, 1),
            self.number_to_le(32, 1),
            self.number_to_le(0, 8),
            self.number_to_le(fee, 8),  # string for 32 bit php
            self.number_to_le(len(self.apiKey), 1),
            self.encode(self.apiKey),
            self.number_to_le(sideNum, 1),
            self.number_to_le(typeNum, 1),
            self.number_to_le(len(normalSymbol), 1),
            self.encode(normalSymbol),
            self.number_to_le(self.integer_divide(amountChain, eightBytes), 8),
            self.number_to_le(self.integer_modulo(amountChain, eightBytes), 8),
            self.number_to_le(self.integer_divide(priceChain, eightBytes), 8),
            self.number_to_le(self.integer_modulo(priceChain, eightBytes), 8),
            self.number_to_le(0, 2),
            self.number_to_le(int(math.floor(now / 1000)), 4),
            self.number_to_le(int(math.floor(expiration / 1000)), 4),
            self.number_to_le(0, 2),
            self.number_to_le(int(quoteId), 4),
            self.number_to_le(int(baseId), 4),
            self.number_to_le(0, 1),
            self.number_to_le(1, 1),
            self.number_to_le(len(dappId), 1),
            self.encode(dappId),
            self.number_to_le(0, 1),
        ]
        txByteStringArray = [
            self.number_to_le(int(math.floor(now / 1000)), 4),
            self.number_to_le(1, 1),
            self.number_to_le(int(math.floor(expiration / 1000)), 4),
            self.number_to_le(1, 1),
            self.number_to_le(32, 1),
            self.number_to_le(0, 8),
            self.number_to_le(fee, 8),  # string for 32 bit php
            self.number_to_le(len(self.apiKey), 1),
            self.encode(self.apiKey),
            self.number_to_le(sideNum, 1),
            self.number_to_le(typeNum, 1),
            self.number_to_le(len(normalSymbol), 1),
            self.encode(normalSymbol),
            self.number_to_le(self.integer_divide(amountChain, eightBytes), 8),
            self.number_to_le(self.integer_modulo(amountChain, eightBytes), 8),
            self.number_to_le(self.integer_divide(priceChain, eightBytes), 8),
            self.number_to_le(self.integer_modulo(priceChain, eightBytes), 8),
            self.number_to_le(0, 2),
            self.number_to_le(int(math.floor(now / 1000)), 4),
            self.number_to_le(int(math.floor(expiration / 1000)), 4),
            self.number_to_le(0, 2),
            self.number_to_le(int(quoteId), 4),
            self.number_to_le(int(baseId), 4),
            self.number_to_le(0, 1),
            self.number_to_le(1, 1),
            self.number_to_le(len(dappId), 1),
            self.encode(dappId),
            self.number_to_le(0, 1),
        ]
        txbytestring = self.binary_concat_array(txByteStringArray)
        txidhash = self.hash(txbytestring, 'sha256', 'hex')
        txid = txidhash[0:40]
        orderidByteStringArray = [
            self.number_to_le(len(txid), 1),
            self.encode(txid),
            self.number_to_be(0, 4),
        ]
        orderidbytestring = self.binary_concat_array(orderidByteStringArray)
        orderidhash = self.hash(orderidbytestring, 'sha256', 'hex')
        orderid = orderidhash[0:40]
        bytestring = self.binary_concat_array(allByteStringArray)
        hash = self.hash(bytestring, 'sha256', 'hex')
        signature = self.ecdsa(hash, self.secret, 'secp256k1', None, True)
        recoveryParam = self.decode(base64.b16encode(self.number_to_le(self.sum(signature['v'], 31), 1)))
        mySignature = recoveryParam + signature['r'] + signature['s']
        operation = {
            'now': datetime,
            'expiration': expirationDatetime,
            'fee': fee,
            'creator': self.apiKey,
            'side': sideNum,
            'order_type': typeNum,
            'market_name': normalSymbol,
            'amount': amountChain,
            'price': priceChain,
            'use_btt_as_fee': False,
            'money_id': int(quoteId),
            'stock_id': int(baseId),
        }
        fatty = {
            'timestamp': datetime,
            'expiration': expirationDatetime,
            'operations': [
                [
                    32,
                    operation,
                ],
            ],
            'validate_type': 0,
            'dapp': dappId,
            'signatures': [
                mySignature,
            ],
        }
        request = {
            'trObj': self.json(fatty),
        }
        response = self.publicPostTransactionCreateorder(request)
        timestamp = self.milliseconds()
        statusCode = self.safe_string(response, 'code')
        status = 'open' if (statusCode == '0') else 'failed'
        return {
            'info': response,
            'id': orderid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': status,
            'symbol': None,
            'type': None,
            'side': None,
            'price': None,
            'amount': None,
            'filled': None,
            'remaining': None,
            'cost': None,
            'trades': None,
            'fee': None,
            'clientOrderId': None,
            'average': None,
        }

    def fetch_order(self, id, symbol=None, params={}):
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired('fetchOrder requires self.apiKey or userid argument')
        self.load_markets()
        request = {
            'userid': self.apiKey,
        }
        market = None
        if symbol is not None:
            market = self.markets[symbol]
            request['symbol'] = market['id']
        request['id'] = id
        response = self.publicGetOrders(self.extend(request, params))
        return self.parse_order(response, market)

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired('fetchOpenOrders requires self.apiKey or userid argument')
        self.load_markets()
        request = {
            'userid': self.apiKey,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetOrdersOpen(self.extend(request, params))
        return self.parse_orders(response, market, since, limit)

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired('fetchClosedOrders requires self.apiKey or userid argument')
        self.load_markets()
        market = None
        request = {
            'userid': self.apiKey,
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetOrdersClosed(self.extend(request, params))
        return self.parse_orders(response, market, since, limit)

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired('fetchOrders requires self.apiKey or userid argument')
        self.load_markets()
        market = None
        request = {
            'userid': self.apiKey,
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetOrdersAll(self.extend(request, params))
        return self.parse_orders(response, market, since, limit)

    def cancel_order(self, id, symbol=None, params={}):
        if self.apiKey is None:
            raise ArgumentsRequired('cancelOrder requires hasAlreadyAuthenticatedSuccessfully')
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        baseId = market['baseId']
        quoteId = market['quoteId']
        normalSymbol = market['normalSymbol']
        feeAmount = '300000000000000'
        now = self.milliseconds()
        expiration = 0
        datetime = self.iso8601(now)
        datetime = datetime.split('.')[0]
        expirationDatetime = self.iso8601(expiration)
        expirationDatetime = expirationDatetime.split('.')[0]
        defaultDappId = 'Sagittarius'
        dappId = self.safe_string(params, 'dappId', defaultDappId)
        byteStringArray = [
            self.number_to_be(1, 32),
            self.number_to_le(int(math.floor(now / 1000)), 4),
            self.number_to_le(1, 1),
            self.number_to_le(expiration, 4),
            self.number_to_le(1, 1),
            self.number_to_le(33, 1),
            self.number_to_le(0, 8),
            self.number_to_le(feeAmount, 8),  # string for 32 bit php
            self.number_to_le(len(self.apiKey), 1),
            self.encode(self.apiKey),
            self.number_to_le(len(normalSymbol), 1),
            self.encode(normalSymbol),
            self.base16_to_binary(id),
            self.number_to_le(int(quoteId), 4),
            self.number_to_le(int(baseId), 4),
            self.number_to_le(0, 1),
            self.number_to_le(1, 1),
            self.number_to_le(len(dappId), 1),
            self.encode(dappId),
            self.number_to_le(0, 1),
        ]
        bytestring = self.binary_concat_array(byteStringArray)
        hash = self.hash(bytestring, 'sha256', 'hex')
        signature = self.ecdsa(hash, self.secret, 'secp256k1', None, True)
        recoveryParam = self.decode(base64.b16encode(self.number_to_le(self.sum(signature['v'], 31), 1)))
        mySignature = recoveryParam + signature['r'] + signature['s']
        operation = {
            'fee': feeAmount,
            'creator': self.apiKey,
            'order_id': id,
            'market_name': normalSymbol,
            'money_id': int(quoteId),
            'stock_id': int(baseId),
        }
        fatty = {
            'timestamp': datetime,
            'expiration': expirationDatetime,
            'operations': [
                [
                    33,
                    operation,
                ],
            ],
            'validate_type': 0,
            'dapp': dappId,
            'signatures': [
                mySignature,
            ],
        }
        request = {
            'trObj': self.json(fatty),
        }
        response = self.publicPostTransactionCancelorder(request)
        timestamp = self.milliseconds()
        statusCode = self.safe_string(response, 'code')
        status = 'canceled' if (statusCode == '0') else 'failed'
        return {
            'info': response,
            'id': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': status,
            'symbol': None,
            'type': None,
            'side': None,
            'price': None,
            'amount': None,
            'filled': None,
            'remaining': None,
            'cost': None,
            'trades': None,
            'fee': None,
            'clientOrderId': None,
            'average': None,
        }

    def transfer(self, code, amount, address, message='', params={}):
        self.check_required_dependencies()
        if self.apiKey is None:
            raise ArgumentsRequired('transfer requires self.apiKey')
        self.load_markets()
        currency = self.currency(code)
        amountTruncate = self.decimal_to_precision(amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING)
        amountChain = self.to_wei(amountTruncate, currency['precision']['amount'])
        assetType = int(currency['id'])
        now = self.milliseconds()
        expiration = now
        datetime = self.iso8601(now)
        datetime = datetime.split('.')[0]
        expirationDatetime = self.iso8601(expiration)
        expirationDatetime = expirationDatetime.split('.')[0]
        feeAmount = '300000000000000'
        defaultDappId = 'Sagittarius'
        dappId = self.safe_string(params, 'dappId', defaultDappId)
        eightBytes = self.integer_pow('2', '64')
        byteStringArray = [
            self.number_to_be(1, 32),
            self.number_to_le(int(math.floor(now / 1000)), 4),
            self.number_to_le(1, 1),
            self.number_to_le(int(math.floor(expiration / 1000)), 4),
            self.number_to_le(1, 1),
            self.number_to_le(28, 1),
            self.number_to_le(0, 8),
            self.number_to_le(feeAmount, 8),  # string for 32 bit php
            self.number_to_le(len(self.apiKey), 1),
            self.encode(self.apiKey),
            self.number_to_le(len(address), 1),
            self.encode(address),
            self.number_to_le(assetType, 4),
            self.number_to_le(self.integer_divide(amountChain, eightBytes), 8),
            self.number_to_le(self.integer_modulo(amountChain, eightBytes), 8),
            self.number_to_le(1, 1),
            self.number_to_le(len(message), 1),
            self.encode(message),
            self.number_to_le(0, 1),
            self.number_to_le(1, 1),
            self.number_to_le(len(dappId), 1),
            self.encode(dappId),
            self.number_to_le(0, 1),
        ]
        bytestring = self.binary_concat_array(byteStringArray)
        hash = self.hash(bytestring, 'sha256', 'hex')
        signature = self.ecdsa(hash, self.secret, 'secp256k1', None, True)
        recoveryParam = self.decode(base64.b16encode(self.number_to_le(self.sum(signature['v'], 31), 1)))
        mySignature = recoveryParam + signature['r'] + signature['s']
        operation = {
            'fee': '300000000000000',
            'from': self.apiKey,
            'to': address,
            'asset_type': int(currency['id']),
            'amount': str(amountChain),
            'message': message,
        }
        fatty = {
            'timestamp': datetime,
            'expiration': expirationDatetime,
            'operations': [
                [
                    28,
                    operation,
                ],
            ],
            'validate_type': 0,
            'dapp': dappId,
            'signatures': [
                mySignature,
            ],
        }
        request = {
            'trObj': self.json(fatty),
        }
        response = self.publicPostTransactionTransfer(request)
        timestamp = self.milliseconds()
        statusCode = self.safe_string(response, 'code')
        status = ''
        if statusCode == '0':
            status = 'submit success'
        else:
            status = 'submit fail'
        return {
            'info': response,
            'id': '',
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': status,
            'symbol': None,
            'type': None,
            'side': None,
            'price': None,
            'amount': None,
            'filled': None,
            'remaining': None,
            'cost': None,
            'fee': None,
            'clientOrderId': None,
            'average': None,
            'trades': None,
        }

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired('fetchMyTrades requires self.apiKey or userid argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'userid': self.apiKey,
        }
        if symbol is not None:
            request['symbol'] = market['id']
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetOrdersTrades(self.extend(request, params))
        return self.parse_trades(response, market, since, limit)

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        self.load_markets()
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired('fetchDeposits requires self.apiKey or userid argument')
        currency = None
        request = {
            'userid': self.apiKey,
        }
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['since'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetDeposits(self.extend(request, params))
        return self.parse_transactions(response, currency, since, limit)

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        self.load_markets()
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired('fetchWithdrawals requires self.apiKey or userid argument')
        currency = None
        request = {
            'userid': self.apiKey,
        }
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['since'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetWithdrawals(self.extend(request, params))
        return self.parse_transactions(response, currency, since, limit)

    def parse_transaction_status(self, status):
        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 self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction, currency=None):
        id = self.safe_string(transaction, 'id')
        address = self.safe_string(transaction, 'address')
        tag = self.safe_string(transaction, 'tag')
        if tag is not None:
            if len(tag) < 1:
                tag = None
        txid = self.safe_value(transaction, 'txid')
        currencyId = self.safe_string(transaction, 'code')
        code = self.safe_currency_code(currencyId, currency)
        timestamp = self.safe_integer(transaction, 'timestamp')
        datetime = self.safe_string(transaction, 'datetime')
        type = self.safe_string(transaction, 'type')
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        amount = self.safe_float(transaction, 'amount')
        feeInfo = self.safe_value(transaction, 'fee')
        feeCost = self.safe_float(feeInfo, 'cost')
        feeCurrencyId = self.safe_string(feeInfo, 'code')
        feeCode = self.safe_currency_code(feeCurrencyId, currency)
        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': None,
            'fee': fee,
        }

    def fetch_deposit_address(self, code, params={}):
        self.load_markets()
        if not ('userid' in params) and (self.apiKey is None):
            raise ArgumentsRequired('fetchDepositAddress requires self.apiKey or userid argument')
        currency = self.currency(code)
        request = {
            'userid': self.apiKey,
            'code': currency['id'],
        }
        response = self.publicGetDepositaddress(request)
        address = self.safe_string(response[0], 'address')
        tag = self.safe_string(response[0], 'tag')
        chainType = self.safe_string(response[0], 'chainType')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'chainType': chainType,
            'info': response,
        }

    def withdraw(self, code, amount, address, tag=None, params={}):
        self.check_required_dependencies()
        self.check_address(address)
        self.load_markets()
        if self.apiKey is None:
            raise ArgumentsRequired('withdraw requires self.apiKey')
        addressResponse = self.fetch_deposit_address(code)
        chainTypeString = self.safe_string(addressResponse, 'chainType')
        chainId = self.safe_string(addressResponse['info'][0], 'chainId')
        middleAddress = ''
        if chainTypeString == 'eos':
            middleAddress = address
        else:
            middleAddress = self.safe_string(addressResponse, 'address')
        operationId = 18
        if chainTypeString != 'ethereum' and chainTypeString != 'etc' and chainTypeString != 'eos' and chainTypeString != 'cmt' and chainTypeString != 'naka':
            operationId = 26
        now = self.milliseconds()
        expiration = 0
        datetime = self.iso8601(now)
        datetime = datetime.split('.')[0]
        expirationDatetime = self.iso8601(expiration)
        expirationDatetime = expirationDatetime.split('.')[0]
        defaultDappId = 'Sagittarius'
        dappId = self.safe_string(params, 'dappId', defaultDappId)
        feeAmount = '300000000000000'
        currency = self.currency(code)
        coinId = currency['id']
        amountTruncate = self.decimal_to_precision(amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING)
        amountChain = self.to_wei(amountTruncate, currency['info']['externalPrecision'])
        eightBytes = self.integer_pow('2', '64')
        assetFee = 0
        byteStringArray = []
        if operationId == 26:
            assetFee = currency['info']['fee']
            byteStringArray = [
                self.number_to_be(1, 32),
                self.number_to_le(int(math.floor(now / 1000)), 4),
                self.number_to_le(1, 1),
                self.number_to_le(int(math.floor(expiration / 1000)), 4),
                self.number_to_le(1, 1),
                self.number_to_le(operationId, 1),
                self.number_to_le(0, 8),
                self.number_to_le(feeAmount, 8),  # string for 32 bit php
                self.number_to_le(len(self.apiKey), 1),
                self.encode(self.apiKey),
                self.number_to_le(len(address), 1),
                self.encode(address),
                self.number_to_le(int(coinId), 4),
                self.number_to_le(self.integer_divide(amountChain, eightBytes), 8),
                self.number_to_le(self.integer_modulo(amountChain, eightBytes), 8),
                self.number_to_le(1, 1),
                self.number_to_le(self.integer_divide(assetFee, eightBytes), 8),
                self.number_to_le(self.integer_modulo(assetFee, eightBytes), 8),
                self.number_to_le(0, 1),
                self.number_to_le(1, 1),
                self.number_to_le(len(dappId), 1),
                self.encode(dappId),
                self.number_to_le(0, 1),
            ]
        else:
            byteStringArray = [
                self.number_to_be(1, 32),
                self.number_to_le(int(math.floor(now / 1000)), 4),
                self.number_to_le(1, 1),
                self.number_to_le(int(math.floor(expiration / 1000)), 4),
                self.number_to_le(1, 1),
                self.number_to_le(operationId, 1),
                self.number_to_le(0, 8),
                self.number_to_le(feeAmount, 8),  # string for 32 bit php
                self.number_to_le(len(self.apiKey), 1),
                self.encode(self.apiKey),
                self.number_to_le(int(math.floor(now / 1000)), 4),
                self.number_to_le(1, 1),
                self.number_to_le(4, 1),
                self.number_to_le(0, 8),
                self.number_to_le(feeAmount, 8),
                self.number_to_le(len(self.apiKey), 1),
                self.encode(self.apiKey),
                self.number_to_le(len(middleAddress), 1),
                self.encode(middleAddress),
                self.number_to_le(int(coinId), 4),
                self.number_to_le(self.integer_divide(amountChain, eightBytes), 8),
                self.number_to_le(self.integer_modulo(amountChain, eightBytes), 8),
                self.number_to_le(0, 1),
                self.number_to_le(1, 1),
                self.number_to_le(len(dappId), 1),
                self.encode(dappId),
                self.number_to_le(0, 1),
            ]
        bytestring = self.binary_concat_array(byteStringArray)
        hash = self.hash(bytestring, 'sha256', 'hex')
        signature = self.ecdsa(hash, self.secret, 'secp256k1', None, True)
        recoveryParam = self.decode(base64.b16encode(self.number_to_le(self.sum(signature['v'], 31), 1)))
        mySignature = recoveryParam + signature['r'] + signature['s']
        fatty = None
        request = None
        operation = None
        chainContractAddress = self.safe_string(currency['info'], 'chainContractAddress')
        if operationId == 26:
            operation = {
                'fee': feeAmount,
                'from': self.apiKey,
                'to_external_address': address,
                'asset_type': int(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': self.json(fatty),
                'chainContractAddress': chainContractAddress,
            }
        else:
            operation = {
                'fee': feeAmount,
                'from': self.apiKey,
                'to_external_address': middleAddress,
                'asset_type': int(coinId),
                'amount': amountChain,
                'asset_fee': assetFee,
            }
            middle = {
                'fee': feeAmount,
                'proposaler': self.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': self.json(fatty),
                    'chainContractAddress': chainContractAddress,
                }
            else:
                request = {
                    'chainType': chainId,
                    'toExternalAddress': address,
                    'trObj': self.json(fatty),
                    'chainContractAddress': chainContractAddress,
                }
        response = self.publicPostTransactionWithdraw(request)
        return {
            'info': response,
            'id': self.safe_string(response, 'id'),
        }

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'][api]
        url += '/' + path
        if params:
            url += '?' + self.urlencode(params)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if code == 503:
            raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body)
        if response is None:
            return  # fallback to default error handler
        if 'code' in response:
            status = self.safe_string(response, 'code')
            if status == '1':
                message = self.safe_string(response, 'msg')
                feedback = self.id + ' ' + body
                self.throw_exactly_matched_exception(self.exceptions, message, feedback)
                raise ExchangeError(feedback)
