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

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

from ccxt_zagrava.base.exchange import Exchange
from ccxt_zagrava.abstract.wazirx import ImplicitAPI
import hashlib
from ccxt_zagrava.base.types import Balances, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import PermissionDenied
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import RateLimitExceeded
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class wazirx(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(wazirx, self).describe(), {
            'id': 'wazirx',
            'name': 'WazirX',
            'countries': ['IN'],
            'version': 'v2',
            'rateLimit': 1000,
            'pro': True,
            'has': {
                'CORS': False,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'closeAllPositions': False,
                'closePosition': False,
                'createOrder': True,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'fetchBalance': True,
                'fetchBidsAsks': False,
                'fetchBorrowInterest': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': False,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': True,
                'fetchDepositsWithdrawals': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchIsolatedPositions': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarginMode': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': False,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': False,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTransactionFees': False,
                'fetchTransactions': False,
                'fetchTransfers': False,
                'fetchWithdrawals': True,
                'reduceMargin': False,
                'repayCrossMargin': False,
                'repayIsolatedMargin': False,
                'setLeverage': False,
                'setMargin': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'transfer': False,
                'withdraw': False,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/148647666-c109c20b-f8ac-472f-91c3-5f658cb90f49.jpeg',
                'api': {
                    'rest': 'https://api.wazirx.com/sapi/v1',
                },
                'www': 'https://wazirx.com',
                'doc': 'https://docs.wazirx.com/#public-rest-api-for-wazirx',
                'fees': 'https://wazirx.com/fees',
                'referral': 'https://wazirx.com/invite/k7rrnks5',
            },
            'api': {
                'public': {
                    'get': {
                        'exchangeInfo': 1,
                        'depth': 0.5,
                        'ping': 1,
                        'systemStatus': 1,
                        'tickers/24hr': 1,
                        'ticker/24hr': 1,
                        'time': 1,
                        'trades': 1,
                        'klines': 1,
                    },
                },
                'private': {
                    'get': {
                        'account': 1,
                        'allOrders': 1,
                        'funds': 1,
                        'historicalTrades': 1,
                        'openOrders': 1,
                        'order': 0.5,
                        'myTrades': 0.5,
                        'coins': 12,
                        'crypto/withdraws': 12,
                        'crypto/deposits/address': 60,
                        'sub_account/fund_transfer/history': 1,
                        'sub_account/accounts': 1,
                    },
                    'post': {
                        'order': 0.1,
                        'order/test': 0.5,
                    },
                    'delete': {
                        'order': 0.1,
                        'openOrders': 1,
                    },
                },
            },
            'fees': {
                'WRX': {'maker': self.parse_number('0.0'), 'taker': self.parse_number('0.0')},
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'exact': {
                    '-1121': BadSymbol,  # {"code": -1121, "message": "Invalid symbol."}
                    '1999': BadRequest,  # {"code":1999,"message":"symbol is missing, symbol does not have a valid value"} message varies depending on the error
                    '2002': InsufficientFunds,  # {"code":2002,"message":"Not enough USDT balance to execute self order"}
                    '2005': BadRequest,  # {"code":2005,"message":"Signature is incorrect."}
                    '2078': PermissionDenied,  # {"code":2078,"message":"Permission denied."}
                    '2098': BadRequest,  # {"code":2098,"message":"Request out of receiving window."}
                    '2031': InvalidOrder,  # {"code":2031,"message":"Minimum buy amount must be worth 2.0 USDT"}
                    '2113': BadRequest,  # {"code":2113,"message":"RecvWindow must be in range 1..60000"}
                    '2115': BadRequest,  # {"code":2115,"message":"Signature not found."}
                    '2136': RateLimitExceeded,  # {"code":2136,"message":"Too many api request"}
                    '94001': InvalidOrder,  # {"code":94001,"message":"Stop price not found."}
                },
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1d',
                '1w': '1w',
            },
            'options': {
                # 'fetchTradesMethod': 'privateGetHistoricalTrades',
                'recvWindow': 10000,
                'networks': {
                    # You can get network from fetchCurrencies
                },
            },
        })

    def fetch_markets(self, params={}) -> List[Market]:
        """
        :see: https://docs.wazirx.com/#exchange-info
        retrieves data on all markets for wazirx
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = self.publicGetExchangeInfo(params)
        #
        # {
        #     "timezone":"UTC",
        #     "serverTime":1641336850932,
        #     "symbols":[
        #     {
        #         "symbol":"btcinr",
        #         "status":"trading",
        #         "baseAsset":"btc",
        #         "quoteAsset":"inr",
        #         "baseAssetPrecision":5,
        #         "quoteAssetPrecision":0,
        #         "orderTypes":[
        #             "limit",
        #             "stop_limit"
        #         ],
        #         "isSpotTradingAllowed":true,
        #         "filters":[
        #             {
        #                 "filterType":"PRICE_FILTER",
        #                 "minPrice":"1",
        #                 "tickSize":"1"
        #             }
        #         ]
        #     },
        #
        markets = self.safe_value(response, 'symbols', [])
        return self.parse_markets(markets)

    def parse_market(self, market: dict) -> Market:
        id = self.safe_string(market, 'symbol')
        baseId = self.safe_string(market, 'baseAsset')
        quoteId = self.safe_string(market, 'quoteAsset')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        isSpot = self.safe_value(market, 'isSpotTradingAllowed')
        filters = self.safe_value(market, 'filters')
        minPrice: Num = None
        for j in range(0, len(filters)):
            filter = filters[j]
            filterType = self.safe_string(filter, 'filterType')
            if filterType == 'PRICE_FILTER':
                minPrice = self.safe_number(filter, 'minPrice')
        fee = self.safe_value(self.fees, quote, {})
        takerString: Str = self.safe_string(fee, 'taker', '0.2')
        takerString = Precise.string_div(takerString, '100')
        makerString: Str = self.safe_string(fee, 'maker', '0.2')
        makerString = Precise.string_div(makerString, '100')
        status = self.safe_string(market, 'status')
        return {
            'id': id,
            'symbol': base + '/' + quote,
            'base': base,
            'quote': quote,
            'settle': None,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': None,
            'type': 'spot',
            'spot': isSpot,
            'margin': False,
            'swap': False,
            'future': False,
            'option': False,
            'active': (status == 'trading'),
            'contract': False,
            'linear': None,
            'inverse': None,
            'taker': self.parse_number(takerString),
            'maker': self.parse_number(makerString),
            'contractSize': None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'baseAssetPrecision'))),
                'price': self.parse_number(self.parse_precision(self.safe_string(market, 'quoteAssetPrecision'))),
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': None,
                },
                'price': {
                    'min': minPrice,
                    'max': None,
                },
                'amount': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': None,
            'info': market,
        }

    def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        :see: https://docs.wazirx.com/#kline-candlestick-data
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents. Available values [1m,5m,15m,30m,1h,2h,4h,6h,12h,1d,1w]
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in s of the latest candle to fetch
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'interval': self.safe_string(self.timeframes, timeframe, timeframe),
        }
        if limit is not None:
            request['limit'] = min(limit, 2000)
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if since is not None:
            request['startTime'] = self.parse_to_int(since / 1000)
        if until is not None:
            request['endTime'] = until
        response = self.publicGetKlines(self.extend(request, params))
        #
        #    [
        #        [1669014360,1402001,1402001,1402001,1402001,0],
        #        ...
        #    ]
        #
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #    [1669014300,1402001,1402001,1402001,1402001,0],
        #
        return [
            self.safe_timestamp(ohlcv, 0),
            self.safe_number(ohlcv, 1),
            self.safe_number(ohlcv, 2),
            self.safe_number(ohlcv, 3),
            self.safe_number(ohlcv, 4),
            self.safe_number(ohlcv, 5),
        ]

    def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        :see: https://docs.wazirx.com/#order-book
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # [1, 5, 10, 20, 50, 100, 500, 1000]
        response = self.publicGetDepth(self.extend(request, params))
        #
        #     {
        #          "timestamp":1559561187,
        #          "asks":[
        #                     ["8540.0","1.5"],
        #                     ["8541.0","0.0042"]
        #                 ],
        #          "bids":[
        #                     ["8530.0","0.8814"],
        #                     ["8524.0","1.4"]
        #                 ]
        #      }
        #
        timestamp = self.safe_integer(response, 'timestamp')
        return self.parse_order_book(response, symbol, timestamp)

    def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        :see: https://docs.wazirx.com/#24hr-ticker-price-change-statistics
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        ticker = self.publicGetTicker24hr(self.extend(request, params))
        #
        # {
        #     "symbol":"wrxinr",
        #     "baseAsset":"wrx",
        #     "quoteAsset":"inr",
        #     "openPrice":"94.77",
        #     "lowPrice":"92.7",
        #     "highPrice":"95.17",
        #     "lastPrice":"94.03",
        #     "volume":"1118700.0",
        #     "bidPrice":"94.02",
        #     "askPrice":"94.03",
        #     "at":1641382455000
        # }
        #
        return self.parse_ticker(ticker, market)

    def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        :see: https://docs.wazirx.com/#24hr-tickers-price-change-statistics
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        tickers = self.publicGetTickers24hr()
        #
        # [
        #     {
        #        "symbol":"btcinr",
        #        "baseAsset":"btc",
        #        "quoteAsset":"inr",
        #        "openPrice":"3698486",
        #        "lowPrice":"3641155.0",
        #        "highPrice":"3767999.0",
        #        "lastPrice":"3713212.0",
        #        "volume":"254.11582",
        #        "bidPrice":"3715021.0",
        #        "askPrice":"3715022.0",
        #     }
        #     ...
        # ]
        #
        result: dict = {}
        for i in range(0, len(tickers)):
            ticker = tickers[i]
            parsedTicker = self.parse_ticker(ticker)
            symbol = parsedTicker['symbol']
            result[symbol] = parsedTicker
        return self.filter_by_array_tickers(result, 'symbol', symbols)

    def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        :see: https://docs.wazirx.com/#recent-trades-list
        get the list of most recent trades for a particular symbol
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = min(limit, 1000)  # Default 500; max 1000.
        method = self.safe_string(self.options, 'fetchTradesMethod', 'publicGetTrades')
        response = None
        if method == 'privateGetHistoricalTrades':
            response = self.privateGetHistoricalTrades(self.extend(request, params))
        else:
            response = self.publicGetTrades(self.extend(request, params))
        # [
        #     {
        #         "id":322307791,
        #         "price":"93.7",
        #         "qty":"0.7",
        #         "quoteQty":"65.59",
        #         "time":1641386701000,
        #         "isBuyerMaker":false
        #     },
        # ]
        return self.parse_trades(response, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        #     {
        #         "id":322307791,
        #         "price":"93.7",
        #         "qty":"0.7",
        #         "quoteQty":"65.59",
        #         "time":1641386701000,
        #         "isBuyerMaker":false
        #     }
        #
        id = self.safe_string(trade, 'id')
        timestamp = self.safe_integer(trade, 'time')
        datetime = self.iso8601(timestamp)
        market = self.safe_market(None, market)
        isBuyerMaker = self.safe_value(trade, 'isBuyerMaker')
        side = 'sell' if isBuyerMaker else 'buy'
        price = self.safe_number(trade, 'price')
        amount = self.safe_number(trade, 'qty')
        cost = self.safe_number(trade, 'quoteQty')
        return self.safe_trade({
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': datetime,
            'symbol': market['symbol'],
            'order': id,
            'type': None,
            'side': side,
            'takerOrMaker': None,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': None,
        }, market)

    def fetch_status(self, params={}):
        """
        :see: https://docs.wazirx.com/#system-status
        the latest known information on the availability of the exchange API
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `status structure <https://docs.ccxt_zagrava.com/#/?id=exchange-status-structure>`
        """
        response = self.publicGetSystemStatus(params)
        #
        #     {
        #         "status":"normal",  # normal, system maintenance
        #         "message":"System is running normally."
        #     }
        #
        status = self.safe_string(response, 'status')
        return {
            'status': 'ok' if (status == 'normal') else 'maintenance',
            'updated': None,
            'eta': None,
            'url': None,
            'info': response,
        }

    def fetch_time(self, params={}):
        """
        :see: https://docs.wazirx.com/#check-server-time
        fetches the current integer timestamp in milliseconds from the exchange server
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = self.publicGetTime(params)
        #
        #     {
        #         "serverTime":1635467280514
        #     }
        #
        return self.safe_integer(response, 'serverTime')

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        #     {
        #        "symbol":"btcinr",
        #        "baseAsset":"btc",
        #        "quoteAsset":"inr",
        #        "openPrice":"3698486",
        #        "lowPrice":"3641155.0",
        #        "highPrice":"3767999.0",
        #        "lastPrice":"3713212.0",
        #        "volume":"254.11582",  # base volume
        #        "bidPrice":"3715021.0",
        #        "askPrice":"3715022.0",
        #        "at":1641382455000  # only on fetchTicker
        #     }
        #
        marketId = self.safe_string(ticker, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        last = self.safe_string(ticker, 'lastPrice')
        open = self.safe_string(ticker, 'openPrice')
        high = self.safe_string(ticker, 'highPrice')
        low = self.safe_string(ticker, 'lowPrice')
        baseVolume = self.safe_string(ticker, 'volume')
        bid = self.safe_string(ticker, 'bidPrice')
        ask = self.safe_string(ticker, 'askPrice')
        timestamp = self.safe_integer(ticker, 'at')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': high,
            'low': low,
            'bid': bid,
            'bidVolume': None,
            'ask': ask,
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': None,
            'info': ticker,
        }, market)

    def parse_balance(self, response) -> Balances:
        result: dict = {'info': response}
        for i in range(0, len(response)):
            balance = response[i]
            id = self.safe_string(balance, 'asset')
            code = self.safe_currency_code(id)
            account = self.account()
            account['free'] = self.safe_string(balance, 'free')
            account['used'] = self.safe_string(balance, 'locked')
            result[code] = account
        return self.safe_balance(result)

    def fetch_balance(self, params={}) -> Balances:
        """
        :see: https://docs.wazirx.com/#fund-details-user_data
        query for balance and get the amount of funds available for trading or funds locked in orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        self.load_markets()
        response = self.privateGetFunds(params)
        #
        # [
        #       {
        #          "asset":"inr",
        #          "free":"0.0",
        #          "locked":"0.0"
        #       },
        # ]
        #
        return self.parse_balance(response)

    def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://docs.wazirx.com/#all-orders-user_data
        fetches information on multiple orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetAllOrders(self.extend(request, params))
        #
        #   [
        #       {
        #           "id": 28,
        #           "symbol": "wrxinr",
        #           "price": "9293.0",
        #           "origQty": "10.0",
        #           "executedQty": "8.2",
        #           "status": "cancel",
        #           "type": "limit",
        #           "side": "sell",
        #           "createdTime": 1499827319559,
        #           "updatedTime": 1499827319559
        #       },
        #       {
        #           "id": 30,
        #           "symbol": "wrxinr",
        #           "price": "9293.0",
        #           "stopPrice": "9200.0",
        #           "origQty": "10.0",
        #           "executedQty": "0.0",
        #           "status": "cancel",
        #           "type": "stop_limit",
        #           "side": "sell",
        #           "createdTime": 1499827319559,
        #           "updatedTime": 1507725176595
        #       }
        #   ]
        #
        orders = self.parse_orders(response, market, since, limit)
        orders = self.filter_by(orders, 'symbol', symbol)
        return orders

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://docs.wazirx.com/#current-open-orders-user_data
        fetch all unfilled currently open orders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        request: dict = {}
        market: Market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = self.privateGetOpenOrders(self.extend(request, params))
        # [
        #     {
        #         "id": 28,
        #         "symbol": "wrxinr",
        #         "price": "9293.0",
        #         "origQty": "10.0",
        #         "executedQty": "8.2",
        #         "status": "cancel",
        #         "type": "limit",
        #         "side": "sell",
        #         "createdTime": 1499827319559,
        #         "updatedTime": 1499827319559
        #     },
        #     {
        #         "id": 30,
        #         "symbol": "wrxinr",
        #         "price": "9293.0",
        #         "stopPrice": "9200.0",
        #         "origQty": "10.0",
        #         "executedQty": "0.0",
        #         "status": "cancel",
        #         "type": "stop_limit",
        #         "side": "sell",
        #         "createdTime": 1499827319559,
        #         "updatedTime": 1507725176595
        #     }
        # ]
        orders = self.parse_orders(response, market, since, limit)
        return orders

    def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        :see: https://docs.wazirx.com/#cancel-all-open-orders-on-a-symbol-trade
        cancel all open orders in a market
        :param str symbol: unified market symbol of the market to cancel orders in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = self.privateDeleteOpenOrders(self.extend(request, params))
        #
        #    [
        #        {
        #            id: "4565421197",
        #            symbol: "adausdt",
        #            type: "limit",
        #            side: "buy",
        #            status: "wait",
        #            price: "0.41",
        #            origQty: "11.00",
        #            executedQty: "0.00",
        #            avgPrice: "0.00",
        #            createdTime: "1718089507000",
        #            updatedTime: "1718089507000",
        #            clientOrderId: "93d2a838-e272-405d-91e7-3a7bc6d3a003"
        #        }
        #    ]
        #
        return self.parse_orders(response)

    def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        :see: https://docs.wazirx.com/#cancel-order-trade
        cancels an open order
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'orderId': id,
        }
        response = self.privateDeleteOrder(self.extend(request, params))
        return self.parse_order(response)

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        :see: https://docs.wazirx.com/#new-order-trade
        create a trade order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        type = type.lower()
        if (type != 'limit') and (type != 'stop_limit'):
            raise ExchangeError(self.id + ' createOrder() supports limit and stop_limit orders only')
        if price is None:
            raise ExchangeError(self.id + ' createOrder() requires a price argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'side': side,
            'quantity': amount,
            'type': 'limit',
        }
        request['price'] = self.price_to_precision(symbol, price)
        stopPrice = self.safe_string(params, 'stopPrice')
        if stopPrice is not None:
            request['type'] = 'stop_limit'
            request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
        response = self.privatePostOrder(self.extend(request, params))
        # {
        #     "id": 28,
        #     "symbol": "wrxinr",
        #     "price": "9293.0",
        #     "origQty": "10.0",
        #     "executedQty": "8.2",
        #     "status": "wait",
        #     "type": "limit",
        #     "side": "sell",
        #     "createdTime": 1499827319559,
        #     "updatedTime": 1499827319559
        # }
        return self.parse_order(response, market)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        #    {
        #        "id": 1949417813,
        #        "symbol": "ltcusdt",
        #        "type": "limit",
        #        "side": "sell",
        #        "status": "done",
        #        "price": "146.2",
        #        "origQty": "0.05",
        #        "executedQty": "0.05",
        #        "avgPrice":  "0.00",
        #        "createdTime": 1641252564000,
        #        "updatedTime": 1641252564000
        #        "clientOrderId": "93d2a838-e272-405d-91e7-3a7bc6d3a003"
        #    }
        #
        created = self.safe_integer(order, 'createdTime')
        updated = self.safe_integer(order, 'updatedTime')
        marketId = self.safe_string(order, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        amount = self.safe_string(order, 'quantity')
        filled = self.safe_string(order, 'executedQty')
        status = self.parse_order_status(self.safe_string(order, 'status'))
        id = self.safe_string(order, 'id')
        price = self.safe_string(order, 'price')
        type = self.safe_string_lower(order, 'type')
        side = self.safe_string_lower(order, 'side')
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': self.safe_string(order, 'clientOrderId'),
            'timestamp': created,
            'datetime': self.iso8601(created),
            'lastTradeTimestamp': updated,
            'status': status,
            'symbol': symbol,
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': price,
            'amount': amount,
            'filled': filled,
            'remaining': None,
            'cost': None,
            'fee': None,
            'average': self.safe_string(order, 'avgPrice'),
            'trades': [],
        }, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'wait': 'open',
            'done': 'closed',
            'cancel': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :see: https://docs.wazirx.com/#all-coins-39-information-user_data
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        if not self.check_required_credentials(False):
            return None
        response = self.privateGetCoins(params)
        #
        #     [
        #         {
        #             "currency": "btc",
        #             "name": "Bitcoin",
        #             "networkList": [
        #                 {
        #                     "addressRegex": "^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$|^(bc1)[0-9A-Za-z]{39,59}$",
        #                     "confirmations": 4,
        #                     "depositDesc": {
        #                         "description": ""
        #                     },
        #                     "depositDust": "0.00000001",
        #                     "depositEnable": True,
        #                     "disclaimer": "• \u003cb\u003eSend only using the Bitcoin network.\u003c/b\u003e Using any other network will result in loss of funds.\u003cbr/\u003e• \u003cb\u003eDeposit only BTC to self deposit address.\u003c/b\u003e Depositing any other asset will result in a loss of funds.\u003cbr/\u003e",
        #                     "fullName": null,
        #                     "hidden": {
        #                         "deposit": False,
        #                         "withdraw": False
        #                     },
        #                     "isDefault": True,
        #                     "maxWithdrawAmount": "3",
        #                     "minConfirm": 4,
        #                     "minWithdrawAmount": "0.003",
        #                     "name": "Bitcoin",
        #                     "network": "btc",
        #                     "order": 3,
        #                     "precision": 8,
        #                     "requestId": "6d67a13d-26f7-4941-9856-94eba4adfe78",
        #                     "shortName": "BTC",
        #                     "specialTip": "Please ensure to select \u003cb\u003eBitcoin\u003c/b\u003e network at sender's wallet.",
        #                     "withdrawConsent": {
        #                         "helpUrl": null,
        #                         "message": "I confirm that self withdrawal of crypto assets is being done to my own wallet, above. I authorize you to share travel rule information with the destination wallet service provider wherever applicable."
        #                     },
        #                     "withdrawDesc": {
        #                         "description": ""
        #                     },
        #                     "withdrawEnable": True,
        #                     "withdrawFee": "0.0015"
        #                 }
        #             ],
        #             "rapidListed": False
        #         }
        #     ]
        #
        result: dict = {}
        for i in range(0, len(response)):
            currency = response[i]
            currencyId = self.safe_string(currency, 'currency')
            code = self.safe_currency_code(currencyId)
            name = self.safe_string(currency, 'name')
            chains = self.safe_list(currency, 'networkList', [])
            networks: dict = {}
            minPrecision = None
            minWithdrawFeeString = None
            minWithdrawString = None
            maxWithdrawString = None
            minDepositString = None
            deposit = False
            withdraw = False
            for j in range(0, len(chains)):
                chain = chains[j]
                networkId = self.safe_string(chain, 'network')
                networkCode = self.network_id_to_code(networkId)
                precision = self.parse_number(self.parse_precision(self.safe_string(chain, 'precision')))
                minPrecision = precision if (minPrecision is None) else min(minPrecision, precision)
                depositAllowed = self.safe_bool(chain, 'depositEnable')
                deposit = depositAllowed if (depositAllowed) else deposit
                withdrawAllowed = self.safe_bool(chain, 'withdrawEnable')
                withdraw = withdrawAllowed if (withdrawAllowed) else withdraw
                withdrawFeeString = self.safe_string(chain, 'withdrawFee')
                if withdrawFeeString is not None:
                    minWithdrawFeeString = withdrawFeeString if (minWithdrawFeeString is None) else Precise.string_min(withdrawFeeString, minWithdrawFeeString)
                minNetworkWithdrawString = self.safe_string(chain, 'minWithdrawAmount')
                if minNetworkWithdrawString is not None:
                    minWithdrawString = minNetworkWithdrawString if (minWithdrawString is None) else Precise.string_min(minNetworkWithdrawString, minWithdrawString)
                maxNetworkWithdrawString = self.safe_string(chain, 'maxWithdrawAmount')
                if maxNetworkWithdrawString is not None:
                    maxWithdrawString = maxNetworkWithdrawString if (maxWithdrawString is None) else Precise.string_min(maxNetworkWithdrawString, maxWithdrawString)
                minNetworkDepositString = self.safe_string(chain, 'depositDust')
                if minNetworkDepositString is not None:
                    minDepositString = minNetworkDepositString if (minDepositString is None) else Precise.string_min(minNetworkDepositString, minDepositString)
                networks[networkCode] = {
                    'info': chain,
                    'id': networkId,
                    'network': networkCode,
                    'active': depositAllowed and withdrawAllowed,
                    'deposit': depositAllowed,
                    'withdraw': withdrawAllowed,
                    'fee': self.parse_number(withdrawFeeString),
                    'precision': precision,
                    'limits': {
                        'withdraw': {
                            'min': self.parse_number(minNetworkWithdrawString),
                            'max': self.parse_number(maxNetworkWithdrawString),
                        },
                        'deposit': {
                            'min': self.parse_number(minNetworkDepositString),
                            'max': None,
                        },
                    },
                }
            result[code] = {
                'info': currency,
                'code': code,
                'id': currencyId,
                'name': name,
                'active': deposit and withdraw,
                'deposit': deposit,
                'withdraw': withdraw,
                'fee': self.parse_number(minWithdrawFeeString),
                'precision': minPrecision,
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': self.parse_number(minWithdrawString),
                        'max': self.parse_number(maxWithdrawString),
                    },
                    'deposit': {
                        'min': self.parse_number(minDepositString),
                        'max': None,
                    },
                },
                'networks': networks,
            }
        return result

    def fetch_deposit_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :see: https://docs.wazirx.com/#deposit-address-supporting-network-user_data
        :param str code: unified currency code of the currency for the deposit address
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.network]: unified network code, you can get network from fetchCurrencies
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        networkCode = self.safe_string(params, 'network')
        params = self.omit(params, 'network')
        if networkCode is None:
            raise ArgumentsRequired(self.id + ' fetchDepositAddress() requires a network parameter')
        request: dict = {
            'coin': currency['id'],
            'network': self.network_code_to_id(networkCode, code),
        }
        response = self.privateGetCryptoDepositsAddress(self.extend(request, params))
        #
        #     {
        #         "address": "bc1qrzpyzh69pfclpqy7c3yg8rkjsy49se7642v4q3",
        #         "coin": "btc",
        #         "url": "https:  #live.blockcypher.com/btc/address/bc1qrzpyzh69pfclpqy7c3yg8rkjsy49se7642v4q3"
        #     }
        #
        return {
            'currency': code,
            'address': self.safe_string(response, 'address'),
            'tag': None,
            'network': self.network_code_to_id(networkCode, code),
            'info': response,
        }

    def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account
        :see: https://docs.wazirx.com/#withdraw-history-supporting-network-user_data
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch withdrawals for
        :param int [limit]: the maximum number of withdrawals structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        self.load_markets()
        request: dict = {}
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if since is not None:
            request['startTime'] = since
        if until is not None:
            request['endTime'] = until
        response = self.privateGetCryptoWithdraws(self.extend(request, params))
        #
        #     [
        #         {
        #             "address": "0x94df8b352de7f46f64b01d3666bf6e936e44ce60",
        #             "amount": "8.91000000",
        #             "createdAt": "2019-10-12 09:12:02",
        #             "lastUpdated": "2019-10-12 11:12:02",
        #             "coin": "USDT",
        #             "id": "b6ae22b3aa844210a7041aee7589627c",
        #             "withdrawOrderId": "WITHDRAWtest123",
        #             "network": "ETH",
        #             "status": 1,
        #             "transactionFee": "0.004",
        #             "failureInfo":"The address is not valid. Please confirm with the recipient",
        #             "txId": "0xb5ef8c13b968a406cc62a93a8bd80f9e9a906ef1b3fcf20a2e48573c17659268"
        #         }
        #     ]
        #
        return self.parse_transactions(response, currency, since, limit)

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            '0': 'ok',
            '1': 'fail',
            '2': 'pending',
            '3': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        #     {
        #         "address": "0x94df8b352de7f46f64b01d3666bf6e936e44ce60",
        #         "amount": "8.91000000",
        #         "createdAt": "2019-10-12 09:12:02",
        #         "lastUpdated": "2019-10-12 11:12:02",
        #         "coin": "USDT",
        #         "id": "b6ae22b3aa844210a7041aee7589627c",
        #         "withdrawOrderId": "WITHDRAWtest123",
        #         "network": "ETH",
        #         "status": 1,
        #         "transactionFee": "0.004",
        #         "failureInfo": "The address is not valid. Please confirm with the recipient",
        #         "txId": "0xb5ef8c13b968a406cc62a93a8bd80f9e9a906ef1b3fcf20a2e48573c17659268"
        #     }
        #
        currencyId = self.safe_string(transaction, 'coin')
        code = self.safe_currency_code(currencyId, currency)
        timestamp = self.parse8601(self.safe_string(transaction, 'createdAt'))
        updated = self.parse8601(self.safe_string(transaction, 'lastUpdated'))
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        feeCost = self.safe_number(transaction, 'transactionFee')
        fee = None
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': code,
            }
        return {
            'info': transaction,
            'id': self.safe_string(transaction, 'id'),
            'txid': self.safe_string(transaction, 'txId'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': self.network_id_to_code(self.safe_string(transaction, 'network')),
            'address': self.safe_string(transaction, 'address'),
            'addressTo': self.safe_string(transaction, 'address'),
            'addressFrom': None,
            'tag': None,
            'tagTo': None,
            'tagFrom': None,
            'type': 'withdrawal',
            'amount': self.safe_number(transaction, 'amount'),
            'currency': code,
            'status': status,
            'updated': updated,
            'fee': fee,
            'internal': None,
            'comment': None,
        }

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api']['rest'] + '/' + path
        if api == 'public':
            if params:
                url += '?' + self.urlencode(params)
        if api == 'private':
            self.check_required_credentials()
            timestamp = self.milliseconds()
            data = self.extend({'recvWindow': self.options['recvWindow'], 'timestamp': timestamp}, params)
            data = self.keysort(data)
            signature = self.hmac(self.encode(self.urlencode(data)), self.encode(self.secret), hashlib.sha256)
            url += '?' + self.urlencode(data)
            url += '&' + 'signature=' + signature
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'X-Api-Key': self.apiKey,
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        #
        # {"code":2098,"message":"Request out of receiving window."}
        #
        if response is None:
            return None
        errorCode = self.safe_string(response, 'code')
        if errorCode is not None:
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
            raise ExchangeError(feedback)
        return None
