text: # -*- 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 hashlib
  from ccxt.base.errors import ExchangeError
  from ccxt.base.errors import AuthenticationError
  from ccxt.base.errors import BadRequest
  from ccxt.base.errors import InsufficientFunds
  from ccxt.base.errors import InvalidAddress
  from ccxt.base.errors import InvalidOrder
  from ccxt.base.errors import NotSupported
  from ccxt.base.errors import DDoSProtection
  from ccxt.base.errors import ExchangeNotAvailable
  from ccxt.base.decimal_to_precision import ROUND
  from ccxt.base.decimal_to_precision import TRUNCATE
  from ccxt.base.decimal_to_precision import DECIMAL_PLACES
  from ccxt.base.decimal_to_precision import TICK_SIZE
  from ccxt.base.decimal_to_precision import PAD_WITH_ZERO
  from ccxt.base.precise import Precise
  
  
  class idex(Exchange):
  
      def describe(self):
          return self.deep_extend(super(idex, self).describe(), {
              'id': 'idex',
              'name': 'IDEX',
              'countries': ['US'],
              # public data endpoints 5 requests a second => 1000ms / 5 = 200ms between requests roughly(without Authentication)
              # all endpoints 10 requests a second =>(1000ms / rateLimit) / 10 => 1 / 2(with Authentication)
              'rateLimit': 200,
              'version': 'v3',
              'pro': True,
              'certified': True,
              'requiresWeb3': True,
              'has': {
                  'CORS': None,
                  'spot': True,
                  'margin': False,
                  'swap': False,
                  'future': False,
                  'option': False,
                  'addMargin': False,
                  'cancelOrder': True,
                  'createOrder': True,
                  'createReduceOnlyOrder': False,
                  'createStopLimitOrder': True,
                  'createStopMarketOrder': True,
                  'createStopOrder': True,
                  'fetchBalance': True,
                  'fetchBorrowRate': False,
                  'fetchBorrowRateHistories': False,
                  'fetchBorrowRateHistory': False,
                  'fetchBorrowRates': False,
                  'fetchBorrowRatesPerSymbol': False,
                  'fetchClosedOrders': True,
                  'fetchCurrencies': True,
                  'fetchDeposits': True,
                  'fetchFundingHistory': False,
                  'fetchFundingRate': False,
                  'fetchFundingRateHistory': False,
                  'fetchFundingRates': False,
                  'fetchIndexOHLCV': False,
                  'fetchLeverage': False,
                  'fetchLeverageTiers': False,
                  'fetchMarkets': True,
                  'fetchMarkOHLCV': False,
                  'fetchMyTrades': True,
                  'fetchOHLCV': True,
                  'fetchOpenInterestHistory': False,
                  'fetchOpenOrders': True,
                  'fetchOrder': True,
                  'fetchOrderBook': True,
                  'fetchOrders': None,
                  'fetchPosition': False,
                  'fetchPositions': False,
                  'fetchPositionsRisk': False,
                  'fetchPremiumIndexOHLCV': False,
                  'fetchTicker': True,
                  'fetchTickers': True,
                  'fetchTrades': True,
                  'fetchTradingFee': False,
                  'fetchTradingFees': True,
                  'fetchTransactions': None,
                  'fetchWithdrawals': True,
                  'reduceMargin': False,
                  'setLeverage': False,
                  'setMarginMode': False,
                  'setPositionMode': False,
                  'transfer': False,
                  'withdraw': True,
              },
              'timeframes': {
                  '1m': '1m',
                  '5m': '5m',
                  '15m': '15m',
                  '30m': '30m',
                  '1h': '1h',
                  '6h': '6h',
                  '1d': '1d',
              },
              'urls': {
                  'test': {
                      'MATIC': 'https://api-sandbox-matic.idex.io',
                  },
                  'logo': 'https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg',
                  'api': {
                      'MATIC': 'https://api-matic.idex.io',
                  },
                  'www': 'https://idex.io',
                  'doc': [
                      'https://docs.idex.io/',
                  ],
              },
              'api': {
                  'public': {
                      'get': {
                          'ping': 1,
                          'time': 1,
                          'exchange': 1,
                          'assets': 1,
                          'markets': 1,
                          'tickers': 1,
                          'candles': 1,
                          'trades': 1,
                          'orderbook': 1,
                      },
                  },
                  'private': {
                      'get': {
                          'user': 1,
                          'wallets': 1,
                          'balances': 1,
                          'orders': 1,
                          'fills': 1,
                          'deposits': 1,
                          'withdrawals': 1,
                          'wsToken': 1,
                      },
                      'post': {
                          'wallets': 1,
                          'orders': 1,
                          'orders/test': 1,
                          'withdrawals': 1,
                      },
                      'delete': {
                          'orders': 1,
                      },
                  },
              },
              'options': {
                  'defaultTimeInForce': 'gtc',
                  'defaultSelfTradePrevention': 'cn',
                  'network': 'MATIC',
              },
              'exceptions': {
                  'INVALID_ORDER_QUANTITY': InvalidOrder,
                  'INSUFFICIENT_FUNDS': InsufficientFunds,
                  'SERVICE_UNAVAILABLE': ExchangeNotAvailable,
                  'EXCEEDED_RATE_LIMIT': DDoSProtection,
                  'INVALID_PARAMETER': BadRequest,
                  'WALLET_NOT_ASSOCIATED': InvalidAddress,
                  'INVALID_WALLET_SIGNATURE': AuthenticationError,
              },
              'requiredCredentials': {
                  'walletAddress': True,
                  'privateKey': True,
                  'apiKey': True,
                  'secret': True,
              },
              'precisionMode': TICK_SIZE,
              'paddingMode': PAD_WITH_ZERO,
              'commonCurrencies': {},
          })
  
      def price_to_precision(self, symbol, price):
          #
          # we override priceToPrecision to fix the following issue
          # https://github.com/ccxt/ccxt/issues/13367
          # {"code":"INVALID_PARAMETER","message":"invalid value provided for request parameter \"price\": all quantities and prices must be below 100 billion, above 0, need to be provided as strings, and always require 4 decimals ending with 4 zeroes"}
          #
          market = self.market(symbol)
          info = self.safe_value(market, 'info', {})
          quoteAssetPrecision = self.safe_integer(info, 'quoteAssetPrecision')
          price = self.decimal_to_precision(price, ROUND, market['precision']['price'], self.precisionMode)
          return self.decimal_to_precision(price, TRUNCATE, quoteAssetPrecision, DECIMAL_PLACES, PAD_WITH_ZERO)
  
      def fetch_markets(self, params={}):
          """
          retrieves data on all markets for idex
          :param dict params: extra parameters specific to the exchange api endpoint
          :returns [dict]: an array of objects representing market data
          """
          response = self.publicGetMarkets(params)
          #
          # [
          #   {
          #     market: 'DIL-ETH',
          #     status: 'active',
          #     baseAsset: 'DIL',
          #     baseAssetPrecision: 8,
          #     quoteAsset: 'ETH',
          #     quoteAssetPrecision: 8
          #   }, ...
          # ]
          #
          response2 = self.publicGetExchange()
          #
          # {
          #     "timeZone": "UTC",
          #     "serverTime": 1590408000000,
          #     "ethereumDepositContractAddress": "0x...",
          #     "ethUsdPrice": "206.46",
          #     "gasPrice": 7,
          #     "volume24hUsd": "10416227.98",
          #     "makerFeeRate": "0.001",
          #     "takerFeeRate": "0.002",
          #     "makerTradeMinimum": "0.15000000",
          #     "takerTradeMinimum": "0.05000000",
          #     "withdrawalMinimum": "0.04000000",
          #     "tickSize":"0.00001000"
          # }
          #
          maker = self.safe_number(response2, 'makerFeeRate')
          taker = self.safe_number(response2, 'takerFeeRate')
          makerMin = self.safe_string(response2, 'makerTradeMinimum')
          takerMin = self.safe_string(response2, 'takerTradeMinimum')
          minCostETH = self.parse_number(Precise.string_min(makerMin, takerMin))
          result = []
          for i in range(0, len(response)):
              entry = response[i]
              marketId = self.safe_string(entry, 'market')
              baseId = self.safe_string(entry, 'baseAsset')
              quoteId = self.safe_string(entry, 'quoteAsset')
              base = self.safe_currency_code(baseId)
              quote = self.safe_currency_code(quoteId)
              basePrecisionString = self.safe_string(entry, 'baseAssetPrecision')
              quotePrecisionString = self.safe_string(entry, 'quoteAssetPrecision')
              basePrecision = self.parse_number(self.parse_precision(basePrecisionString))
              quotePrecision = self.parse_number(self.parse_precision(quotePrecisionString))
              quotePrecision = self.safe_number(entry, 'tickSize', quotePrecision)
              status = self.safe_string(entry, 'status')
              minCost = None
              if quote == 'ETH':
                  minCost = minCostETH
              result.append({
                  'id': marketId,
                  'symbol': base + '/' + quote,
                  'base': base,
                  'quote': quote,
                  'settle': None,
                  'baseId': baseId,
                  'quoteId': quoteId,
                  'settleId': None,
                  'type': 'spot',
                  'spot': True,
                  'margin': False,
                  'swap': False,
                  'future': False,
                  'option': False,
                  'active': (status != 'inactive'),
                  'contract': False,
                  'linear': None,
                  'inverse': None,
                  'taker': taker,
                  'maker': maker,
                  'contractSize': None,
                  'expiry': None,
                  'expiryDatetime': None,
                  'strike': None,
                  'optionType': None,
                  'precision': {
                      'amount': basePrecision,
                      'price': quotePrecision,
                  },
                  'limits': {
                      'leverage': {
                          'min': None,
                          'max': None,
                      },
                      'amount': {
                          'min': basePrecision,
                          'max': None,
                      },
                      'price': {
                          'min': quotePrecision,
                          'max': None,
                      },
                      'cost': {
                          'min': minCost,
                          'max': None,
                      },
                  },
                  'info': entry,
              })
          return result
  
      def fetch_ticker(self, symbol, params={}):
          """
          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 idex api endpoint
          :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
          """
          self.load_markets()
          market = self.market(symbol)
          request = {
              'market': market['id'],
          }
          # [
          #   {
          #     market: 'DIL-ETH',
          #     time: 1598367493008,
          #     open: '0.09695361',
          #     high: '0.10245881',
          #     low: '0.09572507',
          #     close: '0.09917079',
          #     closeQuantity: '0.71320950',
          #     baseVolume: '309.17380612',
          #     quoteVolume: '30.57633981',
          #     percentChange: '2.28',
          #     numTrades: 205,
          #     ask: '0.09910476',
          #     bid: '0.09688340',
          #     sequence: 3902
          #   }
          # ]
          response = self.publicGetTickers(self.extend(request, params))
          ticker = self.safe_value(response, 0)
          return self.parse_ticker(ticker, market)
  
      def fetch_tickers(self, symbols=None, params={}):
          """
          fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours 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 idex api endpoint
          :returns dict: an array of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
          """
          self.load_markets()
          # [
          #   {
          #     market: 'DIL-ETH',
          #     time: 1598367493008,
          #     open: '0.09695361',
          #     high: '0.10245881',
          #     low: '0.09572507',
          #     close: '0.09917079',
          #     closeQuantity: '0.71320950',
          #     baseVolume: '309.17380612',
          #     quoteVolume: '30.57633981',
          #     percentChange: '2.28',
          #     numTrades: 205,
          #     ask: '0.09910476',
          #     bid: '0.09688340',
          #     sequence: 3902
          #   }, ...
          # ]
          response = self.publicGetTickers(params)
          return self.parse_tickers(response, symbols)
  
      def parse_ticker(self, ticker, market=None):
          # {
          #   market: 'DIL-ETH',
          #   time: 1598367493008,
          #   open: '0.09695361',
          #   high: '0.10245881',
          #   low: '0.09572507',
          #   close: '0.09917079',
          #   closeQuantity: '0.71320950',
          #   baseVolume: '309.17380612',
          #   quoteVolume: '30.57633981',
          #   percentChange: '2.28',
          #   numTrades: 205,
          #   ask: '0.09910476',
          #   bid: '0.09688340',
          #   sequence: 3902
          # }
          marketId = self.safe_string(ticker, 'market')
          market = self.safe_market(marketId, market, '-')
          symbol = market['symbol']
          timestamp = self.safe_integer(ticker, 'time')
          close = self.safe_string(ticker, 'close')
          return self.safe_ticker({
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'high': self.safe_string(ticker, 'high'),
              'low': self.safe_string(ticker, 'low'),
              'bid': self.safe_string(ticker, 'bid'),
              'bidVolume': None,
              'ask': self.safe_string(ticker, 'ask'),
              'askVolume': None,
              'vwap': None,
              'open': self.safe_string(ticker, 'open'),
              'close': close,
              'last': close,
              'previousClose': None,
              'change': None,
              'percentage': self.safe_string(ticker, 'percentChange'),
              'average': None,
              'baseVolume': self.safe_string(ticker, 'baseVolume'),
              'quoteVolume': self.safe_string(ticker, 'quoteVolume'),
              'info': ticker,
          }, market, False)
  
      def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
          """
          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
          :param int|None since: timestamp in ms of the earliest candle to fetch
          :param int|None limit: the maximum amount of candles to fetch
          :param dict params: extra parameters specific to the idex api endpoint
          :returns [[int]]: A list of candles ordered as timestamp, open, high, low, close, volume
          """
          self.load_markets()
          market = self.market(symbol)
          request = {
              'market': market['id'],
              'interval': timeframe,
          }
          if since is not None:
              request['start'] = since
          if limit is not None:
              request['limit'] = limit
          response = self.publicGetCandles(self.extend(request, params))
          if isinstance(response, list):
              # [
              #   {
              #     start: 1598345580000,
              #     open: '0.09771286',
              #     high: '0.09771286',
              #     low: '0.09771286',
              #     close: '0.09771286',
              #     volume: '1.45340410',
              #     sequence: 3853
              #   }, ...
              # ]
              return self.parse_ohlcvs(response, market, timeframe, since, limit)
          else:
              #  {"nextTime":1595536440000}
              return []
  
      def parse_ohlcv(self, ohlcv, market=None):
          # {
          #   start: 1598345580000,
          #   open: '0.09771286',
          #   high: '0.09771286',
          #   low: '0.09771286',
          #   close: '0.09771286',
          #   volume: '1.45340410',
          #   sequence: 3853
          # }
          timestamp = self.safe_integer(ohlcv, 'start')
          open = self.safe_number(ohlcv, 'open')
          high = self.safe_number(ohlcv, 'high')
          low = self.safe_number(ohlcv, 'low')
          close = self.safe_number(ohlcv, 'close')
          volume = self.safe_number(ohlcv, 'volume')
          return [timestamp, open, high, low, close, volume]
  
      def fetch_trades(self, symbol, since=None, limit=None, params={}):
          """
          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|None since: timestamp in ms of the earliest trade to fetch
          :param int|None limit: the maximum amount of trades to fetch
          :param dict params: extra parameters specific to the idex api endpoint
          :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
          """
          self.load_markets()
          market = self.market(symbol)
          request = {
              'market': market['id'],
          }
          if since is not None:
              request['start'] = since
          if limit is not None:
              request['limit'] = limit
          # [
          #   {
          #     fillId: 'b5467d00-b13e-3fa9-8216-dd66735550fc',
          #     price: '0.09771286',
          #     quantity: '1.45340410',
          #     quoteQuantity: '0.14201627',
          #     time: 1598345638994,
          #     makerSide: 'buy',
          #     sequence: 3853
          #   }, ...
          # ]
          response = self.publicGetTrades(self.extend(request, params))
          return self.parse_trades(response, market, since, limit)
  
      def parse_trade(self, trade, market=None):
          #
          # public trades
          #  {
          #      "fillId":"a4883704-850b-3c4b-8588-020b5e4c62f1",
          #      "price":"0.20377008",
          #      "quantity":"47.58448728",
          #      "quoteQuantity":"9.69629509",
          #      "time":1642091300873,
          #      "makerSide":"buy",
          #      "type":"hybrid",        # one of either: "orderBook", "hybrid", or "pool"
          #      "sequence":31876
          #  }
          #
          # private trades
          #  {
          #      "fillId":"83429066-9334-3582-b710-78858b2f0d6b",
          #      "price":"0.20717368",
          #      "quantity":"15.00000000",
          #      "quoteQuantity":"3.10760523",
          #      "orderBookQuantity":"0.00000003",
          #      "orderBookQuoteQuantity":"0.00000001",
          #      "poolQuantity":"14.99999997",
          #      "poolQuoteQuantity":"3.10760522",
          #      "time":1642083351215,
          #      "makerSide":"sell",
          #      "sequence":31795,
          #      "market":"IDEX-USDC",
          #      "orderId":"4fe993f0-747b-11ec-bd08-79d4a0b6e47c",
          #      "side":"buy",
          #      "fee":"0.03749989",
          #      "feeAsset":"IDEX",
          #      "gas":"0.40507261",
          #      "liquidity":"taker",
          #      "type":"hybrid",
          #      "txId":"0x69f6d82a762d12e3201efd0b3e9cc1969351e3c6ea3cf07c47c66bf24a459815",
          #      "txStatus":"mined"
          #  }
          #
          id = self.safe_string(trade, 'fillId')
          priceString = self.safe_string(trade, 'price')
          amountString = self.safe_string(trade, 'quantity')
          costString = self.safe_string(trade, 'quoteQuantity')
          timestamp = self.safe_integer(trade, 'time')
          marketId = self.safe_string(trade, 'market')
          symbol = self.safe_symbol(marketId, market, '-')
          # self code handles the duality of public vs private trades
          makerSide = self.safe_string(trade, 'makerSide')
          oppositeSide = 'sell' if (makerSide == 'buy') else 'buy'
          side = self.safe_string(trade, 'side', oppositeSide)
          takerOrMaker = self.safe_string(trade, 'liquidity', 'taker')
          feeCostString = self.safe_string(trade, 'fee')
          fee = None
          if feeCostString is not None:
              feeCurrencyId = self.safe_string(trade, 'feeAsset')
              fee = {
                  'cost': feeCostString,
                  'currency': self.safe_currency_code(feeCurrencyId),
              }
          orderId = self.safe_string(trade, 'orderId')
          return self.safe_trade({
              'info': trade,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'symbol': symbol,
              'id': id,
              'order': orderId,
              'type': 'limit',
              'side': side,
              'takerOrMaker': takerOrMaker,
              'price': priceString,
              'amount': amountString,
              'cost': costString,
              'fee': fee,
          }, market)
  
      def fetch_trading_fees(self, params={}):
          self.check_required_credentials()
          self.load_markets()
          nonce = self.uuidv1()
          request = {
              'nonce': nonce,
          }
          response = None
          response = self.privateGetUser(self.extend(request, params))
          #
          #     {
          #         depositEnabled: True,
          #         orderEnabled: True,
          #         cancelEnabled: True,
          #         withdrawEnabled: True,
          #         totalPortfolioValueUsd: '0.00',
          #         makerFeeRate: '0.0000',
          #         takerFeeRate: '0.0025',
          #         takerIdexFeeRate: '0.0005',
          #         takerLiquidityProviderFeeRate: '0.0020'
          #     }
          #
          maker = self.safe_number(response, 'makerFeeRate')
          taker = self.safe_number(response, 'takerFeeRate')
          result = {}
          for i in range(0, len(self.symbols)):
              symbol = self.symbols[i]
              result[symbol] = {
                  'info': response,
                  'symbol': symbol,
                  'maker': maker,
                  'taker': taker,
                  'percentage': True,
                  'tierBased': False,
              }
          return result
  
      def fetch_order_book(self, symbol, limit=None, params={}):
          """
          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|None limit: the maximum amount of order book entries to return
          :param dict params: extra parameters specific to the idex api endpoint
          :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
          """
          self.load_markets()
          market = self.market(symbol)
          request = {
              'market': market['id'],
              'level': 2,
          }
          if limit is not None:
              request['limit'] = limit
          # {
          #   sequence: 36416753,
          #   bids: [
          #     ['0.09672815', '8.22284267', 1],
          #     ['0.09672814', '1.83685554', 1],
          #     ['0.09672143', '4.10962617', 1],
          #     ['0.09658884', '4.03863759', 1],
          #     ['0.09653781', '3.35730684', 1],
          #     ['0.09624660', '2.54163586', 1],
          #     ['0.09617490', '1.93065030', 1]
          #   ],
          #   asks: [
          #     ['0.09910476', '3.22840154', 1],
          #     ['0.09940587', '3.39796593', 1],
          #     ['0.09948189', '4.25088898', 1],
          #     ['0.09958362', '2.42195784', 1],
          #     ['0.09974393', '4.25234367', 1],
          #     ['0.09995250', '3.40192141', 1]
          #   ]
          # }
          response = self.publicGetOrderbook(self.extend(request, params))
          nonce = self.safe_integer(response, 'sequence')
          return {
              'symbol': symbol,
              'timestamp': None,
              'datetime': None,
              'nonce': nonce,
              'bids': self.parse_side(response, 'bids'),
              'asks': self.parse_side(response, 'asks'),
          }
  
      def parse_side(self, book, side):
          bookSide = self.safe_value(book, side, [])
          result = []
          for i in range(0, len(bookSide)):
              order = bookSide[i]
              price = self.safe_number(order, 0)
              amount = self.safe_number(order, 1)
              orderCount = self.safe_integer(order, 2)
              result.append([price, amount, orderCount])
          descending = side == 'bids'
          return self.sort_by(result, 0, descending)
  
      def fetch_currencies(self, params={}):
          """
          fetches all available currencies on an exchange
          :param dict params: extra parameters specific to the idex api endpoint
          :returns dict: an associative dictionary of currencies
          """
          # [
          #   {
          #     name: 'Ether',
          #     symbol: 'ETH',
          #     contractAddress: '0x0000000000000000000000000000000000000000',
          #     assetDecimals: 18,
          #     exchangeDecimals: 8
          #   }, ..
          # ]
          response = self.publicGetAssets(params)
          result = {}
          for i in range(0, len(response)):
              entry = response[i]
              name = self.safe_string(entry, 'name')
              currencyId = self.safe_string(entry, 'symbol')
              precisionString = self.safe_string(entry, 'exchangeDecimals')
              code = self.safe_currency_code(currencyId)
              precision = self.parse_precision(precisionString)
              lot = self.parse_number(precision)
              result[code] = {
                  'id': currencyId,
                  'code': code,
                  'info': entry,
                  'type': None,
                  'name': name,
                  'active': None,
                  'deposit': None,
                  'withdraw': None,
                  'fee': None,
                  'precision': int(precisionString),
                  'limits': {
                      'amount': {'min': lot, 'max': None},
                      'withdraw': {'min': lot, 'max': None},
                  },
              }
          return result
  
      def parse_balance(self, response):
          result = {
              'info': response,
              'timestamp': None,
              'datetime': None,
          }
          for i in range(0, len(response)):
              entry = response[i]
              currencyId = self.safe_string(entry, 'asset')
              code = self.safe_currency_code(currencyId)
              account = self.account()
              account['total'] = self.safe_string(entry, 'quantity')
              account['free'] = self.safe_string(entry, 'availableForTrade')
              account['used'] = self.safe_string(entry, 'locked')
              result[code] = account
          return self.safe_balance(result)
  
      def fetch_balance(self, params={}):
          """
          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 idex api endpoint
          :returns dict: a `balance structure <https://docs.ccxt.com/en/latest/manual.html?#balance-structure>`
          """
          self.check_required_credentials()
          self.load_markets()
          nonce1 = self.uuidv1()
          request = {
              'nonce': nonce1,
              'wallet': self.walletAddress,
          }
          # [
          #   {
          #     asset: 'DIL',
          #     quantity: '0.00000000',
          #     availableForTrade: '0.00000000',
          #     locked: '0.00000000',
          #     usdValue: null
          #   }, ...
          # ]
          extendedRequest = self.extend(request, params)
          if extendedRequest['wallet'] is None:
              raise BadRequest(self.id + ' fetchBalance() wallet is None, set self.walletAddress or "address" in params')
          response = None
          try:
              response = self.privateGetBalances(extendedRequest)
          except Exception as e:
              if isinstance(e, InvalidAddress):
                  walletAddress = extendedRequest['wallet']
                  self.associate_wallet(walletAddress)
                  response = self.privateGetBalances(extendedRequest)
              else:
                  raise e
          return self.parse_balance(response)
  
      def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
          self.check_required_credentials()
          self.load_markets()
          market = None
          request = {
              'nonce': self.uuidv1(),
              'wallet': self.walletAddress,
          }
          if symbol is not None:
              market = self.market(symbol)
              request['market'] = market['id']
          if since is not None:
              request['start'] = since
          if limit is not None:
              request['limit'] = limit
          # [
          #   {
          #     fillId: '48582d10-b9bb-3c4b-94d3-e67537cf2472',
          #     price: '0.09905990',
          #     quantity: '0.40000000',
          #     quoteQuantity: '0.03962396',
          #     time: 1598873478762,
          #     makerSide: 'sell',
          #     sequence: 5053,
          #     market: 'DIL-ETH',
          #     orderId: '7cdc8e90-eb7d-11ea-9e60-4118569f6e63',
          #     side: 'buy',
          #     fee: '0.00080000',
          #     feeAsset: 'DIL',
          #     gas: '0.00857497',
          #     liquidity: 'taker',
          #     txId: '0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65',
          #     txStatus: 'mined'
          #   }
          # ]
          extendedRequest = self.extend(request, params)
          if extendedRequest['wallet'] is None:
              raise BadRequest(self.id + ' fetchMyTrades() walletAddress is None, set self.walletAddress or "address" in params')
          response = None
          try:
              response = self.privateGetFills(extendedRequest)
          except Exception as e:
              if isinstance(e, InvalidAddress):
                  walletAddress = extendedRequest['wallet']
                  self.associate_wallet(walletAddress)
                  response = self.privateGetFills(extendedRequest)
              else:
                  raise e
          return self.parse_trades(response, market, since, limit)
  
      def fetch_order(self, id, symbol=None, params={}):
          request = {
              'orderId': id,
          }
          return self.fetch_orders_helper(symbol, None, None, self.extend(request, params))
  
      def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
          request = {
              'closed': False,
          }
          return self.fetch_orders_helper(symbol, since, limit, self.extend(request, params))
  
      def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
          request = {
              'closed': True,
          }
          return self.fetch_orders_helper(symbol, since, limit, self.extend(request, params))
  
      def fetch_orders_helper(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          request = {
              'nonce': self.uuidv1(),
              'wallet': self.walletAddress,
          }
          market = None
          if symbol is not None:
              market = self.market(symbol)
              request['market'] = market['id']
          if since is not None:
              request['start'] = since
          if limit is not None:
              request['limit'] = limit
          response = self.privateGetOrders(self.extend(request, params))
          # fetchClosedOrders / fetchOpenOrders
          # [
          #   {
          #     "market": "DIL-ETH",
          #     "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63",
          #     "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1",
          #     "time": 1598873478650,
          #     "status": "filled",
          #     "type": "limit",
          #     "side": "buy",
          #     "originalQuantity": "0.40000000",
          #     "executedQuantity": "0.40000000",
          #     "cumulativeQuoteQuantity": "0.03962396",
          #     "avgExecutionPrice": "0.09905990",
          #     "price": "1.00000000",
          #     "fills": [
          #       {
          #         "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472",
          #         "price": "0.09905990",
          #         "quantity": "0.40000000",
          #         "quoteQuantity": "0.03962396",
          #         "time": 1598873478650,
          #         "makerSide": "sell",
          #         "sequence": 5053,
          #         "fee": "0.00080000",
          #         "feeAsset": "DIL",
          #         "gas": "0.00857497",
          #         "liquidity": "taker",
          #         "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65",
          #         "txStatus": "mined"
          #       }
          #     ]
          #   }
          # ]
          # fetchOrder
          # {market: 'DIL-ETH',
          #   orderId: '7cdc8e90-eb7d-11ea-9e60-4118569f6e63',
          #   wallet: '0x0AB991497116f7F5532a4c2f4f7B1784488628e1',
          #   time: 1598873478650,
          #   status: 'filled',
          #   type: 'limit',
          #   side: 'buy',
          #   originalQuantity: '0.40000000',
          #   executedQuantity: '0.40000000',
          #   cumulativeQuoteQuantity: '0.03962396',
          #   avgExecutionPrice: '0.09905990',
          #   price: '1.00000000',
          #   fills:
          #    [{fillId: '48582d10-b9bb-3c4b-94d3-e67537cf2472',
          #        price: '0.09905990',
          #        quantity: '0.40000000',
          #        quoteQuantity: '0.03962396',
          #        time: 1598873478650,
          #        makerSide: 'sell',
          #        sequence: 5053,
          #        fee: '0.00080000',
          #        feeAsset: 'DIL',
          #        gas: '0.00857497',
          #        liquidity: 'taker',
          #        txId: '0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65',
          #        txStatus: 'mined'}]}
          if isinstance(response, list):
              return self.parse_orders(response, market, since, limit)
          else:
              return self.parse_order(response, market)
  
      def parse_order_status(self, status):
          # https://docs.idex.io/#order-states-amp-lifecycle
          statuses = {
              'active': 'open',
              'partiallyFilled': 'open',
              'rejected': 'canceled',
              'filled': 'closed',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_order(self, order, market=None):
          #
          #     {
          #         "market": "DIL-ETH",
          #         "orderId": "7cdc8e90-eb7d-11ea-9e60-4118569f6e63",
          #         "wallet": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1",
          #         "time": 1598873478650,
          #         "status": "filled",
          #         "type": "limit",
          #         "side": "buy",
          #         "originalQuantity": "0.40000000",
          #         "executedQuantity": "0.40000000",
          #         "cumulativeQuoteQuantity": "0.03962396",
          #         "avgExecutionPrice": "0.09905990",
          #         "price": "1.00000000",
          #         "fills": [
          #             {
          #             "fillId": "48582d10-b9bb-3c4b-94d3-e67537cf2472",
          #             "price": "0.09905990",
          #             "quantity": "0.40000000",
          #             "quoteQuantity": "0.03962396",
          #             "time": 1598873478650,
          #             "makerSide": "sell",
          #             "sequence": 5053,
          #             "fee": "0.00080000",
          #             "feeAsset": "DIL",
          #             "gas": "0.00857497",
          #             "liquidity": "taker",
          #             "txId": "0xeaa02b112c0b8b61bc02fa1776a2b39d6c614e287c1af90df0a2e591da573e65",
          #             "txStatus": "mined"
          #             }
          #         ]
          #     }
          #
          timestamp = self.safe_integer(order, 'time')
          fills = self.safe_value(order, 'fills', [])
          id = self.safe_string(order, 'orderId')
          clientOrderId = self.safe_string(order, 'clientOrderId')
          marketId = self.safe_string(order, 'market')
          side = self.safe_string(order, 'side')
          symbol = self.safe_symbol(marketId, market, '-')
          type = self.safe_string(order, 'type')
          amount = self.safe_string(order, 'originalQuantity')
          filled = self.safe_string(order, 'executedQuantity')
          average = self.safe_string(order, 'avgExecutionPrice')
          price = self.safe_string(order, 'price')
          rawStatus = self.safe_string(order, 'status')
          timeInForce = self.safe_string_upper(order, 'timeInForce')
          status = self.parse_order_status(rawStatus)
          return self.safe_order({
              'info': order,
              'id': id,
              'clientOrderId': clientOrderId,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'lastTradeTimestamp': None,
              'symbol': symbol,
              'type': type,
              'timeInForce': timeInForce,
              'postOnly': None,
              'side': side,
              'price': price,
              'stopPrice': None,
              'amount': amount,
              'cost': None,
              'average': average,
              'filled': filled,
              'remaining': None,
              'status': status,
              'fee': None,
              'trades': fills,
          }, market)
  
      def associate_wallet(self, walletAddress, params={}):
          nonce = self.uuidv1()
          noPrefix = self.remove0x_prefix(walletAddress)
          byteArray = [
              self.base16_to_binary(nonce),
              self.base16_to_binary(noPrefix),
          ]
          binary = self.binary_concat_array(byteArray)
          hash = self.hash(binary, 'keccak', 'hex')
          signature = self.sign_message_string(hash, self.privateKey)
          # {
          #   address: '0x0AB991497116f7F5532a4c2f4f7B1784488628e1',
          #   totalPortfolioValueUsd: '0.00',
          #   time: 1598468353626
          # }
          request = {
              'parameters': {
                  'nonce': nonce,
                  'wallet': walletAddress,
              },
              'signature': signature,
          }
          result = self.privatePostWallets(request)
          return result
  
      def create_order(self, symbol, type, side, amount, price=None, params={}):
          # https://docs.idex.io/#create-order
          self.check_required_credentials()
          self.load_markets()
          market = self.market(symbol)
          nonce = self.uuidv1()
          typeEnum = None
          stopLossTypeEnums = {
              'stopLoss': 3,
              'stopLossLimit': 4,
              'takeProfit': 5,
              'takeProfitLimit': 6,
          }
          stopPriceString = None
          if (type == 'stopLossLimit') or (type == 'takeProfitLimit') or ('stopPrice' in params):
              if not ('stopPrice' in params):
                  raise BadRequest(self.id + ' createOrder() stopPrice is a required parameter for ' + type + 'orders')
              stopPriceString = self.price_to_precision(symbol, params['stopPrice'])
          limitTypeEnums = {
              'limit': 1,
              'limitMaker': 2,
          }
          priceString = None
          typeLower = type.lower()
          limitOrder = typeLower.find('limit') >= 0
          if type in limitTypeEnums:
              typeEnum = limitTypeEnums[type]
              priceString = self.price_to_precision(symbol, price)
          elif type in stopLossTypeEnums:
              typeEnum = stopLossTypeEnums[type]
              priceString = self.price_to_precision(symbol, price)
          elif type == 'market':
              typeEnum = 0
          else:
              raise BadRequest(self.id + ' ' + type + ' is not a valid order type')
          amountEnum = 0  # base quantity
          if 'quoteOrderQuantity' in params:
              if type != 'market':
                  raise NotSupported(self.id + ' createOrder() quoteOrderQuantity is not supported for ' + type + ' orders, only supported for market orders')
              amountEnum = 1
              amount = self.safe_number(params, 'quoteOrderQuantity')
          sideEnum = 0 if (side == 'buy') else 1
          walletBytes = self.remove0x_prefix(self.walletAddress)
          network = self.safe_string(self.options, 'network', 'ETH')
          orderVersion = self.get_supported_mapping(network, {
              'ETH': 1,
              'BSC': 2,
              'MATIC': 4,
          })
          amountString = self.amount_to_precision(symbol, amount)
          # https://docs.idex.io/#time-in-force
          timeInForceEnums = {
              'gtc': 0,
              'ioc': 2,
              'fok': 3,
          }
          defaultTimeInForce = self.safe_string(self.options, 'defaultTimeInForce', 'gtc')
          timeInForce = self.safe_string(params, 'timeInForce', defaultTimeInForce)
          timeInForceEnum = None
          if timeInForce in timeInForceEnums:
              timeInForceEnum = timeInForceEnums[timeInForce]
          else:
              allOptions = list(timeInForceEnums.keys())
              asString = ', '.join(allOptions)
              raise BadRequest(self.id + ' ' + timeInForce + ' is not a valid timeInForce, please choose one of ' + asString)
          # https://docs.idex.io/#self-trade-prevention
          selfTradePreventionEnums = {
              'dc': 0,
              'co': 1,
              'cn': 2,
              'cb': 3,
          }
          defaultSelfTradePrevention = self.safe_string(self.options, 'defaultSelfTradePrevention', 'cn')
          selfTradePrevention = self.safe_string(params, 'selfTradePrevention', defaultSelfTradePrevention)
          selfTradePreventionEnum = None
          if selfTradePrevention in selfTradePreventionEnums:
              selfTradePreventionEnum = selfTradePreventionEnums[selfTradePrevention]
          else:
              allOptions = list(selfTradePreventionEnums.keys())
              asString = ', '.join(allOptions)
              raise BadRequest(self.id + ' ' + selfTradePrevention + ' is not a valid selfTradePrevention, please choose one of ' + asString)
          byteArray = [
              self.number_to_be(orderVersion, 1),
              self.base16_to_binary(nonce),
              self.base16_to_binary(walletBytes),
              self.encode(market['id']),  # TODO: refactor to remove either encode or stringToBinary
              self.number_to_be(typeEnum, 1),
              self.number_to_be(sideEnum, 1),
              self.encode(amountString),
              self.number_to_be(amountEnum, 1),
          ]
          if limitOrder:
              encodedPrice = self.encode(priceString)
              byteArray.append(encodedPrice)
          if type in stopLossTypeEnums:
              encodedPrice = self.encode(stopPriceString or priceString)
              byteArray.append(encodedPrice)
          clientOrderId = self.safe_string(params, 'clientOrderId')
          if clientOrderId is not None:
              byteArray.append(self.encode(clientOrderId))
          after = [
              self.number_to_be(timeInForceEnum, 1),
              self.number_to_be(selfTradePreventionEnum, 1),
              self.number_to_be(0, 8),  # unused
          ]
          allBytes = self.array_concat(byteArray, after)
          binary = self.binary_concat_array(allBytes)
          hash = self.hash(binary, 'keccak', 'hex')
          signature = self.sign_message_string(hash, self.privateKey)
          request = {
              'parameters': {
                  'nonce': nonce,
                  'market': market['id'],
                  'side': side,
                  'type': type,
                  'wallet': self.walletAddress,
                  'selfTradePrevention': selfTradePrevention,
              },
              'signature': signature,
          }
          if type != 'market':
              request['parameters']['timeInForce'] = timeInForce
          if limitOrder:
              request['parameters']['price'] = priceString
          if type in stopLossTypeEnums:
              request['parameters']['stopPrice'] = stopPriceString or priceString
          if amountEnum == 0:
              request['parameters']['quantity'] = amountString
          else:
              request['parameters']['quoteOrderQuantity'] = amountString
          if clientOrderId is not None:
              request['parameters']['clientOrderId'] = clientOrderId
          # {
          #   market: 'DIL-ETH',
          #   orderId: '7cdc8e90-eb7d-11ea-9e60-4118569f6e63',
          #   wallet: '0x0AB991497116f7F5532a4c2f4f7B1784488628e1',
          #   time: 1598873478650,
          #   status: 'filled',
          #   type: 'limit',
          #   side: 'buy',
          #   originalQuantity: '0.40000000',
          #   executedQuantity: '0.40000000',
          #   cumulativeQuoteQuantity: '0.03962396',
          #   price: '1.00000000',
          #   fills: [
          #     {
          #       fillId: '48582d10-b9bb-3c4b-94d3-e67537cf2472',
          #       price: '0.09905990',
          #       quantity: '0.40000000',
          #       quoteQuantity: '0.03962396',
          #       time: 1598873478650,
          #       makerSide: 'sell',
          #       sequence: 5053,
          #       fee: '0.00080000',
          #       feeAsset: 'DIL',
          #       gas: '0.00857497',
          #       liquidity: 'taker',
          #       txStatus: 'pending'
          #     }
          #   ],
          #   avgExecutionPrice: '0.09905990'
          # }
          # we don't use self.extend here because it is a signed endpoint
          response = self.privatePostOrders(request)
          return self.parse_order(response, market)
  
      def withdraw(self, code, amount, address, tag=None, params={}):
          tag, params = self.handle_withdraw_tag_and_params(tag, params)
          self.check_required_credentials()
          self.load_markets()
          nonce = self.uuidv1()
          amountString = self.currency_to_precision(code, amount)
          currency = self.currency(code)
          walletBytes = self.remove0x_prefix(self.walletAddress)
          byteArray = [
              self.base16_to_binary(nonce),
              self.base16_to_binary(walletBytes),
              self.encode(currency['id']),
              self.encode(amountString),
              self.number_to_be(1, 1),  # bool set to True
          ]
          binary = self.binary_concat_array(byteArray)
          hash = self.hash(binary, 'keccak', 'hex')
          signature = self.sign_message_string(hash, self.privateKey)
          request = {
              'parameters': {
                  'nonce': nonce,
                  'wallet': address,
                  'asset': currency['id'],
                  'quantity': amountString,
              },
              'signature': signature,
          }
          response = self.privatePostWithdrawals(request)
          #
          #     {
          #         withdrawalId: 'a61dcff0-ec4d-11ea-8b83-c78a6ecb3180',
          #         asset: 'ETH',
          #         assetContractAddress: '0x0000000000000000000000000000000000000000',
          #         quantity: '0.20000000',
          #         time: 1598962883190,
          #         fee: '0.00024000',
          #         txStatus: 'pending',
          #         txId: null
          #     }
          #
          return self.parse_transaction(response, currency)
  
      def cancel_order(self, id, symbol=None, params={}):
          self.check_required_credentials()
          self.load_markets()
          market = None
          if symbol is not None:
              market = self.market(symbol)
          nonce = self.uuidv1()
          walletBytes = self.remove0x_prefix(self.walletAddress)
          byteArray = [
              self.base16_to_binary(nonce),
              self.base16_to_binary(walletBytes),
              self.encode(id),
          ]
          binary = self.binary_concat_array(byteArray)
          hash = self.hash(binary, 'keccak', 'hex')
          signature = self.sign_message_string(hash, self.privateKey)
          request = {
              'parameters': {
                  'nonce': nonce,
                  'wallet': self.walletAddress,
                  'orderId': id,
              },
              'signature': signature,
          }
          # [{orderId: '688336f0-ec50-11ea-9842-b332f8a34d0e'}]
          response = self.privateDeleteOrders(self.extend(request, params))
          canceledOrder = self.safe_value(response, 0)
          return self.parse_order(canceledOrder, market)
  
      def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
          errorCode = self.safe_string(response, 'code')
          message = self.safe_string(response, 'message')
          if errorCode in self.exceptions:
              Exception = self.exceptions[errorCode]
              raise Exception(self.id + ' ' + message)
          if errorCode is not None:
              raise ExchangeError(self.id + ' ' + message)
  
      def fetch_deposits(self, code=None, since=None, limit=None, params={}):
          params = self.extend({
              'method': 'privateGetDeposits',
          }, params)
          return self.fetch_transactions_helper(code, since, limit, params)
  
      def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
          params = self.extend({
              'method': 'privateGetWithdrawals',
          }, params)
          return self.fetch_transactions_helper(code, since, limit, params)
  
      def fetch_transactions_helper(self, code=None, since=None, limit=None, params={}):
          self.load_markets()
          nonce = self.uuidv1()
          request = {
              'nonce': nonce,
              'wallet': self.walletAddress,
          }
          currency = None
          if code is not None:
              currency = self.currency(code)
              request['asset'] = currency['id']
          if since is not None:
              request['start'] = since
          if limit is not None:
              request['limit'] = limit
          # [
          #   {
          #     depositId: 'e9970cc0-eb6b-11ea-9e89-09a5ebc1f98e',
          #     asset: 'ETH',
          #     quantity: '1.00000000',
          #     txId: '0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142',
          #     txTime: 1598865853000,
          #     confirmationTime: 1598865930231
          #   }
          # ]
          method = params['method']
          params = self.omit(params, 'method')
          response = getattr(self, method)(self.extend(request, params))
          return self.parse_transactions(response, currency, since, limit)
  
      def parse_transaction_status(self, status):
          statuses = {
              'mined': 'ok',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_transaction(self, transaction, currency=None):
          #
          # fetchDeposits
          #
          #     {
          #         depositId: 'e9970cc0-eb6b-11ea-9e89-09a5ebc1f98f',
          #         asset: 'ETH',
          #         quantity: '1.00000000',
          #         txId: '0xcd4aac3171d7131cc9e795568c67938675185ac17641553ef54c8a7c294c8142',
          #         txTime: 1598865853000,
          #         confirmationTime: 1598865930231
          #     }
          #
          # fetchWithdrwalas
          #
          #     {
          #         withdrawalId: 'a62d8760-ec4d-11ea-9fa6-47904c19499b',
          #         asset: 'ETH',
          #         assetContractAddress: '0x0000000000000000000000000000000000000000',
          #         quantity: '0.20000000',
          #         time: 1598962883288,
          #         fee: '0.00024000',
          #         txId: '0x305e9cdbaa85ad029f50578d13d31d777c085de573ed5334d95c19116d8c03ce',
          #         txStatus: 'mined'
          #     }
          #
          # withdraw
          #
          #     {
          #         withdrawalId: 'a61dcff0-ec4d-11ea-8b83-c78a6ecb3180',
          #         asset: 'ETH',
          #         assetContractAddress: '0x0000000000000000000000000000000000000000',
          #         quantity: '0.20000000',
          #         time: 1598962883190,
          #         fee: '0.00024000',
          #         txStatus: 'pending',
          #         txId: null
          #     }
          #
          type = None
          if 'depositId' in transaction:
              type = 'deposit'
          elif ('withdrawId' in transaction) or ('withdrawalId' in transaction):
              type = 'withdrawal'
          id = self.safe_string_2(transaction, 'depositId', 'withdrawId')
          id = self.safe_string(transaction, 'withdrawalId', id)
          code = self.safe_currency_code(self.safe_string(transaction, 'asset'), currency)
          amount = self.safe_number(transaction, 'quantity')
          txid = self.safe_string(transaction, 'txId')
          timestamp = self.safe_integer(transaction, 'txTime')
          fee = None
          if 'fee' in transaction:
              fee = {
                  'cost': self.safe_number(transaction, 'fee'),
                  'currency': 'ETH',
              }
          rawStatus = self.safe_string(transaction, 'txStatus')
          status = self.parse_transaction_status(rawStatus)
          updated = self.safe_integer(transaction, 'confirmationTime')
          return {
              'info': transaction,
              'id': id,
              'txid': txid,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'network': None,
              'address': None,
              'addressTo': None,
              'addressFrom': None,
              'tag': None,
              'tagTo': None,
              'tagFrom': None,
              'type': type,
              'amount': amount,
              'currency': code,
              'status': status,
              'updated': updated,
              'fee': fee,
          }
  
      def calculate_rate_limiter_cost(self, api, method, path, params, config={}, context={}):
          hasApiKey = (self.apiKey is not None)
          hasSecret = (self.secret is not None)
          hasWalletAddress = (self.walletAddress is not None)
          hasPrivateKey = (self.privateKey is not None)
          defaultCost = self.safe_value(config, 'cost', 1)
          authenticated = hasApiKey and hasSecret and hasWalletAddress and hasPrivateKey
          return(defaultCost / 2) if authenticated else defaultCost
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          network = self.safe_string(self.options, 'network', 'ETH')
          version = self.safe_string(self.options, 'version', 'v1')
          url = self.urls['api'][network] + '/' + version + '/' + path
          keys = list(params.keys())
          length = len(keys)
          query = None
          if length > 0:
              if method == 'GET':
                  query = self.urlencode(params)
                  url = url + '?' + query
              else:
                  body = self.json(params)
          headers = {
              'Content-Type': 'application/json',
          }
          if self.apiKey is not None:
              headers['IDEX-API-Key'] = self.apiKey
          if api == 'private':
              payload = None
              if method == 'GET':
                  payload = query
              else:
                  payload = body
              headers['IDEX-HMAC-Signature'] = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha256, 'hex')
          return {'url': url, 'method': method, 'body': body, 'headers': headers}
  