# -*- 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.async_support.base.exchange import Exchange
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidAddress
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import DuplicateOrderId
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import InvalidNonce
from ccxt.base.decimal_to_precision import TICK_SIZE


class lbank2(Exchange):

    def describe(self):
        return self.deep_extend(super(lbank2, self).describe(), {
            'id': 'lbank2',
            'name': 'LBank',
            'countries': ['CN'],
            'version': 'v2',
            # 50 per second for making and cancelling orders 1000ms / 50 = 20
            # 20 per second for all other requests, cost = 50 / 20 = 2.5
            'rateLimit': 20,
            'has': {
                'CORS': False,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createOrder': True,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': False,
                'createStopMarketOrder': False,
                'createStopOrder': False,
                'fetchBalance': True,
                'fetchBorrowRate': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchBorrowRatesPerSymbol': False,
                'fetchClosedOrders': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedPositions': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarginMode': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchPosition': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTrades': True,
                'fetchTradingFees': True,
                'fetchTransactionFees': True,
                'reduceMargin': False,
                'setLeverage': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'withdraw': False,
            },
            'timeframes': {
                '1m': 'minute1',
                '5m': 'minute5',
                '15m': 'minute15',
                '30m': 'minute30',
                '1h': 'hour1',
                '2h': 'hour2',
                '4h': 'hour4',
                '6h': 'hour6',
                '8h': 'hour8',
                '12h': 'hour12',
                '1d': 'day1',
                '1w': 'week1',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/38063602-9605e28a-3302-11e8-81be-64b1e53c4cfb.jpg',
                'api': {
                    'rest': 'https://api.lbank.info',
                },
                'api2': 'https://api.lbkex.com',
                'www': 'https://www.lbank.info',
                'doc': 'https://www.lbank.info/en-US/docs/index.html',
                'fees': 'https://lbankinfo.zendesk.com/hc/en-gb/articles/360012072873-Trading-Fees',
                'referral': 'https://www.lbank.info/invitevip?icode=7QCY',
            },
            'api': {
                'public': {
                    'get': {
                        'currencyPairs': 2.5,
                        'accuracy': 2.5,
                        'usdToCny': 2.5,
                        'withdrawConfigs': 2.5,
                        'timestamp': 2.5,
                        'ticker/24hr': 2.5,
                        'ticker': 2.5,
                        'depth': 2.5,
                        'incrDepth': 2.5,
                        'trades': 2.5,
                        'kline': 2.5,
                        # new quote endpoints
                        'supplement/system_ping': 2.5,
                        'supplement/incrDepth': 2.5,
                        'supplement/trades': 2.5,
                        'supplement/ticker/price': 2.5,
                        'supplement/ticker/bookTicker': 2.5,
                    },
                    'post': {
                        'supplement/system_status': 2.5,
                    },
                },
                'private': {
                    'post': {
                        # account
                        'user_info': 2.5,
                        'subscribe/get_key': 2.5,
                        'subscribe/refresh_key': 2.5,
                        'subscribe/destroy_key': 2.5,
                        'get_deposit_address': 2.5,
                        'deposit_history': 2.5,
                        # order
                        'create_order': 1,
                        'batch_create_order': 1,
                        'cancel_order': 1,
                        'cancel_clientOrders': 1,
                        'orders_info': 2.5,
                        'orders_info_history': 2.5,
                        'order_transaction_detail': 2.5,
                        'transaction_history': 2.5,
                        'orders_info_no_deal': 2.5,
                        # withdraw
                        'withdraw': 2.5,
                        'withdrawCancel': 2.5,
                        'withdraws': 2.5,
                        'supplement/user_info': 2.5,
                        'supplement/withdraw': 2.5,
                        'supplement/deposit_history': 2.5,
                        'supplement/withdraws': 2.5,
                        'supplement/get_deposit_address': 2.5,
                        'supplement/asset_detail': 2.5,
                        'supplement/customer_trade_fee': 2.5,
                        'supplement/api_Restrictions': 2.5,
                        # new quote endpoints
                        'supplement/system_ping': 2.5,
                        # new order endpoints
                        'supplement/create_order_test': 1,
                        'supplement/create_order': 1,
                        'supplement/cancel_order': 1,
                        'supplement/cancel_order_by_symbol': 1,
                        'supplement/orders_info': 2.5,
                        'supplement/orders_info_no_deal': 2.5,
                        'supplement/orders_info_history': 2.5,
                        'supplement/user_info_account': 2.5,
                        'supplement/transaction_history': 2.5,
                    },
                },
            },
            'fees': {
                'trading': {
                    'maker': self.parse_number('0.001'),
                    'taker': self.parse_number('0.001'),
                },
                'funding': {
                    'withdraw': {},
                },
            },
            'commonCurrencies': {
                'VET_ERC20': 'VEN',
                'PNT': 'Penta',
            },
            'precisionMode': TICK_SIZE,
            'options': {
                'cacheSecretAsPem': True,
                'createMarketBuyOrderRequiresPrice': True,
                'fetchTrades': {
                    'method': 'publicGetTrades',  # or 'publicGetTradesSupplement'
                },
                'fetchTransactionFees': {
                    'method': 'fetchPrivateTransactionFees',  # or 'fetchPublicTransactionFees'
                },
                'fetchDepositAddress': {
                    'method': 'fetchDepositAddressDefault',  # or fetchDepositAddressSupplement
                },
                'createOrder': {
                    'method': 'privatePostSupplementCreateOrder',  # or privatePostCreateOrder
                },
                'fetchOrder': {
                    'method': 'fetchOrderSupplement',  # or fetchOrderDefault
                },
                'fetchBalance': {
                    'method': 'privatePostSupplementUserInfo',  # or privatePostSupplementUserInfoAccount or privatePostUserInfo
                },
                'networks': {
                    'ERC20': 'erc20',
                    'ETH': 'erc20',
                    'TRC20': 'trc20',
                    'TRX': 'trc20',
                    'OMNI': 'omni',
                    'ASA': 'asa',
                    'BEP20': 'bep20(bsc)',
                    'BSC': 'bep20(bsc)',
                    'HT': 'heco',
                    'BNB': 'bep2',
                    'BTC': 'btc',
                    'DOGE': 'dogecoin',
                    'MATIC': 'matic',
                    'POLYGON': 'matic',
                    'OEC': 'oec',
                    'BTCTRON': 'btctron',
                    'XRP': 'xrp',
                    # other unusual chains with number of listed currencies supported
                    #     'avax c-chain': 1,
                    #     klay: 12,
                    #     bta: 1,
                    #     fantom: 1,
                    #     celo: 1,
                    #     sol: 2,
                    #     zenith: 1,
                    #     ftm: 5,
                    #     bep20: 1,(single token with mis-named chain) SSS
                    #     bitci: 1,
                    #     sgb: 1,
                    #     moonbeam: 1,
                    #     ekta: 1,
                    #     etl: 1,
                    #     arbitrum: 1,
                    #     tpc: 1,
                    #     ptx: 1
                    # }
                },
                'inverse-networks': {
                    'erc20': 'ERC20',
                    'trc20': 'TRC20',
                    'omni': 'OMNI',
                    'asa': 'ASA',
                    'bep20(bsc)': 'BSC',
                    'bep20': 'BSC',
                    'heco': 'HT',
                    'bep2': 'BNB',
                    'btc': 'BTC',
                    'dogecoin': 'DOGE',
                    'matic': 'MATIC',
                    'oec': 'OEC',
                    'btctron': 'BTCTRON',
                    'xrp': 'XRP',
                },
                'defaultNetworks': {
                    'USDT': 'TRC20',
                },
            },
        })

    async def fetch_markets(self, params={}):
        """
        retrieves data on all markets for lbank2
        :param dict params: extra parameters specific to the exchange api endpoint
        :returns [dict]: an array of objects representing market data
        """
        # needs to return a list of unified market structures
        response = await self.publicGetAccuracy()
        data = self.safe_value(response, 'data')
        #      [
        #          {
        #              symbol: 'snx3s_usdt',
        #              quantityAccuracy: '2',
        #              minTranQua: '0.01',
        #              priceAccuracy: '6'
        #          }
        #     ]
        result = []
        for i in range(0, len(data)):
            market = data[i]
            marketId = self.safe_string(market, 'symbol')
            parts = marketId.split('_')
            baseId = parts[0]
            quoteId = parts[1]
            base = baseId.upper()
            quote = quoteId.upper()
            symbol = base + '/' + quote
            productTypes = {
                '3l': True,
                '5l': True,
                '3s': True,
                '5s': True,
            }
            ending = baseId[-2:]
            isLeveragedProduct = self.safe_value(productTypes, ending, False)
            if isLeveragedProduct:
                symbol += ':' + quote
            linear = None
            if isLeveragedProduct is True:
                linear = True
            result.append({
                'id': marketId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'type': 'spot',
                'spot': True,
                'margin': False,
                'swap': isLeveragedProduct,
                'future': False,
                'option': False,
                'active': True,
                'contract': isLeveragedProduct,
                'linear': linear,  # all leveraged ETF products are in USDT
                'inverse': None,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'quantityAccuracy'))),
                    'price': self.parse_number(self.parse_precision(self.safe_string(market, 'priceAccuracy'))),
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': self.safe_number(market, 'minTranQua'),
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'info': market,
            })
        return result

    def parse_ticker(self, ticker, market=None):
        #
        #      {
        #          "symbol":"btc_usdt",
        #          "ticker": {
        #              "high":40200.88,
        #              "vol":7508.3096,
        #              "low":38239.38,
        #              "change":0.75,
        #              "turnover":292962771.34,
        #              "latest":39577.95
        #               },
        #           "timestamp":1647005189792
        #      }
        #
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.safe_integer(ticker, 'timestamp')
        tickerData = self.safe_value(ticker, 'ticker')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(tickerData, 'high'),
            'low': self.safe_string(tickerData, 'low'),
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': None,
            'open': None,
            'close': None,
            'last': self.safe_string(tickerData, 'latest'),
            'previousClose': None,
            'change': None,
            'percentage': self.safe_string(tickerData, 'change'),
            'average': None,
            'baseVolume': self.safe_string(tickerData, 'vol'),
            'quoteVolume': self.safe_string(tickerData, 'turnover'),
            'info': ticker,
        }, market)

    async 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 lbank2 api endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await self.publicGetTicker24hr(self.extend(request, params))
        #
        #      {
        #          "result":"true",
        #          "data": [
        #              {
        #                  "symbol":"btc_usdt",
        #                  "ticker":{
        #                          "high":40200.88,
        #                          "vol":7508.3096,
        #                          "low":38239.38,
        #                          "change":0.75,
        #                          "turnover":292962771.34,
        #                          "latest":39577.95
        #                      },
        #                  "timestamp":1647005189792
        #               }
        #                   ],
        #          "error_code":0,"ts":1647005190755
        #      }
        #
        data = self.safe_value(response, 'data', [])
        first = self.safe_value(data, 0, {})
        return self.parse_ticker(first, market)

    async 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 lbank api endpoint
        :returns dict: an array of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        await self.load_markets()
        request = {
            'symbol': 'all',
        }
        response = await self.publicGetTicker24hr(self.extend(request, params))
        data = self.safe_value(response, 'data', [])
        return self.parse_tickers(data, symbols)

    async 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 lbank2 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
        """
        await self.load_markets()
        market = self.market(symbol)
        if limit is None:
            limit = 60
        request = {
            'symbol': market['id'],
            'size': limit,
        }
        response = await self.publicGetDepth(self.extend(request, params))
        orderbook = response['data']
        timestamp = self.milliseconds()
        return self.parse_order_book(orderbook, market['symbol'], timestamp)

    def parse_trade(self, trade, market=None):
        #
        # fetchTrades(old) publicGetTrades
        #
        #      {
        #          "date_ms":1647021989789,
        #          "amount":0.0028,
        #          "price":38804.2,
        #          "type":"buy",
        #          "tid":"52d5616ee35c43019edddebe59b3e094"
        #      }
        #
        #
        # fetchTrades(new) publicGetTradesSupplement
        #
        #      {
        #          "quoteQty":1675.048485,
        #          "price":0.127545,
        #          "qty":13133,
        #          "id":"3589541dc22e4357b227283650f714e2",
        #          "time":1648058297110,
        #          "isBuyerMaker":false
        #      }
        #
        # fetchMyTrades(private)
        #
        #      {
        #          "orderUuid":"38b4e7a4-14f6-45fd-aba1-1a37024124a0",
        #          "tradeFeeRate":0.0010000000,
        #          "dealTime":1648500944496,
        #          "dealQuantity":30.00000000000000000000,
        #          "tradeFee":0.00453300000000000000,
        #          "txUuid":"11f3850cc6214ea3b495adad3a032794",
        #          "dealPrice":0.15111300000000000000,
        #          "dealVolumePrice":4.53339000000000000000,
        #          "tradeType":"sell_market"
        #      }
        #
        timestamp = self.safe_integer_2(trade, 'date_ms', 'time')
        if timestamp is None:
            timestamp = self.safe_integer(trade, 'dealTime')
        amountString = self.safe_string_2(trade, 'amount', 'qty')
        if amountString is None:
            amountString = self.safe_string(trade, 'dealQuantity')
        priceString = self.safe_string(trade, 'price')
        if priceString is None:
            priceString = self.safe_string(trade, 'dealPrice')
        costString = self.safe_string(trade, 'quoteQty')
        if costString is None:
            costString = self.safe_string(trade, 'dealVolumePrice')
        side = self.safe_string_2(trade, 'tradeType', 'type')
        type = None
        takerOrMaker = None
        if side is not None:
            parts = side.split('_')
            side = self.safe_string(parts, 0)
            typePart = self.safe_string(parts, 1)
            type = 'limit'
            takerOrMaker = 'taker'
            if typePart is not None:
                if typePart == 'market':
                    type = 'market'
                elif typePart == 'maker':
                    takerOrMaker = 'maker'
        id = self.safe_string_2(trade, 'tid', 'id')
        if id is None:
            id = self.safe_string(trade, 'txUuid')
        order = self.safe_string(trade, 'orderUuid')
        symbol = self.safe_symbol(None, market)
        fee = None
        feeCost = self.safe_string(trade, 'tradeFee')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': None,
                'rate': self.safe_string(trade, 'tradeFeeRate'),
            }
        return self.safe_trade({
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'id': id,
            'order': order,
            'type': type,
            'takerOrMaker': takerOrMaker,
            'side': side,
            'price': priceString,
            'amount': amountString,
            'cost': costString,
            'fee': fee,
            'info': trade,
        }, market)

    async 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 lbank2 api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if since is not None:
            request['time'] = since
        if limit is not None:
            request['size'] = limit
        else:
            request['size'] = 600  # max
        method = self.safe_string(params, 'method')
        params = self.omit(params, 'method')
        if method is None:
            options = self.safe_value(self.options, 'fetchTrades', {})
            method = self.safe_string(options, 'method', 'publicGetTrades')
        response = await getattr(self, method)(self.extend(request, params))
        #
        #      {
        #          "result":"true",
        #          "data": [
        #              {
        #                  "date_ms":1647021989789,
        #                  "amount":0.0028,
        #                  "price":38804.2,
        #                  "type":"buy",
        #                  "tid":"52d5616ee35c43019edddebe59b3e094"
        #               }
        #           ],
        #           "error_code":0,
        #           "ts":1647021999308
        #      }
        trades = self.safe_value(response, 'data', [])
        return self.parse_trades(trades, market, since, limit)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #   [
        #     1482311500,  # timestamp
        #     5423.23,    # open
        #     5472.80,    # high
        #     5516.09,    # low
        #     5462,       # close
        #     234.3250    # volume
        #   ],
        #
        return [
            self.safe_timestamp(ohlcv, 0),  # timestamp
            self.safe_number(ohlcv, 1),  # open
            self.safe_number(ohlcv, 2),  # high
            self.safe_number(ohlcv, 3),  # low
            self.safe_number(ohlcv, 4),  # close
            self.safe_number(ohlcv, 5),  # volume
        ]

    async 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 lbank2 api endpoint
        :returns [[int]]: A list of candles ordered as timestamp, open, high, low, close, volume
        """
        # endpoint doesnt work
        await self.load_markets()
        market = self.market(symbol)
        if limit is None:
            limit = 100
        if since is None:
            duration = self.parse_timeframe(timeframe)
            since = self.milliseconds() - duration * 1000 * limit
        request = {
            'symbol': market['id'],
            'type': self.timeframes[timeframe],
            'time': int(since / 1000),
            'size': limit,  # max 2000
        }
        response = await self.publicGetKline(self.extend(request, params))
        ohlcvs = self.safe_value(response, 'data', [])
        #
        #
        # [
        #   [
        #     1482311500,
        #     5423.23,
        #     5472.80,
        #     5516.09,
        #     5462,
        #     234.3250
        #   ],
        #   [
        #     1482311400,
        #     5432.52,
        #     5459.87,
        #     5414.30,
        #     5428.23,
        #     213.7329
        #   ]
        # ]
        #
        return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)

    def parse_balance(self, response):
        #
        # privatePostUserInfo
        #
        #      {
        #          "toBtc": {
        #              "egc:": "0",
        #              "iog": "0",
        #              "ksm": "0",
        #              },
        #          "freeze": {
        #              "egc": "0",
        #              "iog": "0",
        #              "ksm": "0" ,
        #              },
        #          "asset": {
        #              "egc": "0",
        #              "iog": "0",
        #              "ksm": "0",
        #              },
        #          "free": {
        #              "egc": "0",
        #              "iog": "0",
        #              "ksm": "0",
        #              }
        #      }
        #
        # privatePostSupplementUserInfoAccount
        #
        #      {
        #          "balances":[
        #              {
        #                  "asset":"lbk",
        #                  "free":"0",
        #                  "locked":"0"
        #              }, ...
        #          ]
        #      }
        #
        # privatePostSupplementUserInfo
        #
        #      [
        #          {
        #              "usableAmt":"31.45130723",
        #              "assetAmt":"31.45130723",
        #              "networkList":[
        #                  {
        #                      "isDefault":true,
        #                      "withdrawFeeRate":"",
        #                      "name":"bep20(bsc)",
        #                      "withdrawMin":30,
        #                      "minLimit":0.0001,
        #                      "minDeposit":0.0001,
        #                      "feeAssetCode":"doge",
        #                      "withdrawFee":"30",
        #                      "type":1,
        #                      "coin":"doge",
        #                      "network":"bsc"
        #                  },
        #                  {
        #                      "isDefault":false,
        #                      "withdrawFeeRate":"",
        #                      "name":"dogecoin",
        #                      "withdrawMin":10,
        #                      "minLimit":0.0001,
        #                      "minDeposit":10,
        #                      "feeAssetCode":"doge",
        #                      "withdrawFee":"10",
        #                      "type":1,
        #                      "coin":"doge",
        #                      "network":"dogecoin"
        #                  }
        #              ],
        #              "freezeAmt":"0",
        #              "coin":"doge"
        #          }, ...
        #      ]
        #
        timestamp = self.safe_integer(response, 'ts')
        result = {
            'info': response,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }
        data = self.safe_value(response, 'data')
        # from privatePostUserInfo
        toBtc = self.safe_value(data, 'toBtc')
        if toBtc is not None:
            used = self.safe_value(data, 'freeze', {})
            free = self.safe_value(data, 'free', {})
            currencies = list(free.keys())
            for i in range(0, len(currencies)):
                currencyId = currencies[i]
                code = self.safe_currency_code(currencyId)
                account = self.account()
                account['used'] = self.safe_string(used, currencyId)
                account['free'] = self.safe_string(free, currencyId)
                result[code] = account
            return self.safe_balance(result)
        # from privatePostSupplementUserInfoAccount
        balances = self.safe_value(data, 'balances')
        if balances is not None:
            for i in range(0, len(balances)):
                item = balances[i]
                currencyId = self.safe_string(item, 'asset')
                code = self.safe_currency_code(currencyId)
                account = self.account()
                account['free'] = self.safe_string(item, 'free')
                account['used'] = self.safe_string(item, 'locked')
                result[code] = account
            return self.safe_balance(result)
        # from privatePostSupplementUserInfo
        isArray = isinstance(data, list)
        if isArray is True:
            for i in range(0, len(data)):
                item = data[i]
                currencyId = self.safe_string(item, 'coin')
                code = self.safe_currency_code(currencyId)
                account = self.account()
                account['free'] = self.safe_string(item, 'usableAmt')
                account['used'] = self.safe_string(item, 'freezeAmt')
                result[code] = account
            return self.safe_balance(result)

    async 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 lbank2 api endpoint
        :returns dict: a `balance structure <https://docs.ccxt.com/en/latest/manual.html?#balance-structure>`
        """
        await self.load_markets()
        method = self.safe_string(params, 'method')
        if method is None:
            options = self.safe_value(self.options, 'fetchBalance', {})
            method = self.safe_string(options, 'method', 'privatePostSupplementUserInfo')
        response = await getattr(self, method)()
        #
        #    {
        #        "result": "true",
        #        "data": [
        #            {
        #                "usableAmt": "14.36",
        #                "assetAmt": "14.36",
        #                "networkList": [
        #                    {
        #                        "isDefault": False,
        #                        "withdrawFeeRate": "",
        #                        "name": "erc20",
        #                        "withdrawMin": 30,
        #                        "minLimit": 0.0001,
        #                        "minDeposit": 20,
        #                        "feeAssetCode": "usdt",
        #                        "withdrawFee": "30",
        #                        "type": 1,
        #                        "coin": "usdt",
        #                        "network": "eth"
        #                    },
        #                    ...
        #                ],
        #                "freezeAmt": "0",
        #                "coin": "ada"
        #            }
        #        ],
        #        "code": 0
        #    }
        #
        return self.parse_balance(response)

    def parse_trading_fee(self, fee, market=None):
        #
        #      {
        #          "symbol":"skt_usdt",
        #          "makerCommission":"0.10",
        #          "takerCommission":"0.10"
        #      }
        #
        marketId = self.safe_string(fee, 'symbol')
        symbol = self.safe_symbol(marketId)
        return {
            'info': fee,
            'symbol': symbol,
            'maker': self.safe_number(fee, 'makerCommission'),
            'taker': self.safe_number(fee, 'takerCommission'),
        }

    async def fetch_trading_fee(self, symbol, params={}):
        """
        fetch the trading fees for a market
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns dict: a `fee structure <https://docs.ccxt.com/en/latest/manual.html#fee-structure>`
        """
        market = self.market(symbol)
        result = await self.fetch_trading_fees(self.extend(params, {'category': market['id']}))
        return result

    async def fetch_trading_fees(self, params={}):
        """
        fetch the trading fees for multiple markets
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/en/latest/manual.html#fee-structure>` indexed by market symbols
        """
        await self.load_markets()
        request = {}
        response = await self.privatePostSupplementCustomerTradeFee(self.extend(request, params))
        fees = self.safe_value(response, 'data', [])
        result = {}
        for i in range(0, len(fees)):
            fee = self.parse_trading_fee(fees[i])
            symbol = fee['symbol']
            result[symbol] = fee
        return result

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        """
        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|None price: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns dict: an `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        clientOrderId = self.safe_string_2(params, 'custom_id', 'clientOrderId')
        postOnly = self.safe_value(params, 'postOnly', False)
        timeInForce = self.safe_string_upper(params, 'timeInForce')
        params = self.omit(params, ['custom_id', 'clientOrderId', 'timeInForce', 'postOnly'])
        request = {
            'symbol': market['id'],
        }
        ioc = (timeInForce == 'IOC')
        fok = (timeInForce == 'FOK')
        maker = (postOnly or (timeInForce == 'PO'))
        if (type == 'market') and (ioc or fok or maker):
            raise InvalidOrder(self.id + ' createOrder() does not allow market FOK, IOC, or postOnly orders. Only limit IOC, FOK, and postOnly orders are allowed')
        if type == 'limit':
            request['type'] = side
            request['price'] = self.price_to_precision(symbol, price)
            request['amount'] = self.amount_to_precision(symbol, amount)
            if ioc:
                request['type'] = side + '_' + 'ioc'
            elif fok:
                request['type'] = side + '_' + 'fok'
            elif maker:
                request['type'] = side + '_' + 'maker'
        elif type == 'market':
            if side == 'sell':
                request['type'] = side + '_' + 'market'
                request['amount'] = self.amount_to_precision(symbol, amount)
            elif side == 'buy':
                request['type'] = side + '_' + 'market'
                if self.options['createMarketBuyOrderRequiresPrice']:
                    if price is None:
                        raise InvalidOrder(self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply the price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False to supply the cost in the amount argument(the exchange-specific behaviour)")
                    else:
                        cost = float(amount) * float(price)
                        request['price'] = self.price_to_precision(symbol, cost)
                else:
                    request['price'] = amount
        if clientOrderId is not None:
            request['custom_id'] = clientOrderId
        method = None
        method = self.safe_string(params, 'method')
        params = self.omit(params, 'method')
        if method is None:
            options = self.safe_value(self.options, 'createOrder', {})
            method = self.safe_string(options, 'method', 'privatePostSupplementCreateOrder')
        response = await getattr(self, method)(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data":{
        #              "symbol":"doge_usdt",
        #              "order_id":"0cf8a3de-4597-4296-af45-be7abaa06b07"
        #              },
        #          "error_code":0,
        #          "ts":1648162321043
        #      }
        #
        result = self.safe_value(response, 'data', {})
        return {
            'id': self.safe_string(result, 'order_id'),
            'info': result,
        }

    def parse_order_status(self, status):
        statuses = {
            '-1': 'canceled',  # canceled
            '0': 'open',  # not traded
            '1': 'open',  # partial deal
            '2': 'closed',  # complete deal
            '3': 'canceled',  # filled partially and cancelled
            '4': 'closed',  # disposal processing
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        # fetchOrderSupplement(private)
        #
        #      {
        #          "cummulativeQuoteQty":0,
        #          "symbol":"doge_usdt",
        #          "executedQty":0,
        #          "orderId":"53d2d53e-70fb-4398-b722-f48571a5f61e",
        #          "origQty":1E+2,
        #          "price":0.05,
        #          "clientOrderId":null,
        #          "origQuoteOrderQty":5,
        #          "updateTime":1648163406000,
        #          "time":1648163139387,
        #          "type":"buy_maker",
        #          "status":-1
        #      }
        #
        #
        # fetchOrderDefault(private)
        #
        #      {
        #          "symbol":"shib_usdt",
        #          "amount":1,
        #          "create_time":1649367863356,
        #          "price":0.0000246103,
        #          "avg_price":0.00002466180000000104,
        #          "type":"buy_market",
        #          "order_id":"abe8b92d-86d9-4d6d-b71e-d14f5fb53ddf",
        #          "custom_id": "007",                                 # field only present if user creates it at order time
        #          "deal_amount":40548.54065802,
        #          "status":2
        #      }
        #
        # fetchOpenOrders(private)
        #
        #      {
        #          "cummulativeQuoteQty":0,
        #          "symbol":"doge_usdt",
        #          "executedQty":0,
        #          "orderId":"73878edf-008d-4e4c-8041-df1f1b2cd8bb",
        #          "origQty":100,
        #          "price":0.05,
        #          "origQuoteOrderQty":5,
        #          "updateTime":1648501762000,
        #          "time":1648501762353,
        #          "type":"buy",
        #          "status":0
        #      }
        #
        # fetchOrders(private)
        #
        #      {
        #          "cummulativeQuoteQty":0,
        #          "symbol":"doge_usdt",
        #          "executedQty":0,
        #          "orderId":"2cadc7cc-b5f6-486b-a5b4-d6ac49a9c186",
        #          "origQty":100,
        #          "price":0.05,
        #          "origQuoteOrderQty":5,
        #          "updateTime":1648501384000,
        #          "time":1648501363889,
        #          "type":"buy",
        #          "status":-1
        #      }
        #
        id = self.safe_string_2(order, 'orderId', 'order_id')
        clientOrderId = self.safe_string_2(order, 'clientOrderId', 'custom_id')
        timestamp = self.safe_integer_2(order, 'time', 'create_time')
        rawStatus = self.safe_string(order, 'status')
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market)
        timeInForce = None
        postOnly = False
        type = 'limit'
        rawType = self.safe_string(order, 'type')  # buy, sell, buy_market, sell_market, buy_maker,sell_maker,buy_ioc,sell_ioc, buy_fok, sell_fok
        parts = rawType.split('_')
        side = self.safe_string(parts, 0)
        typePart = self.safe_string(parts, 1)  # market, maker, ioc, fok or None(limit)
        if typePart == 'market':
            type = 'market'
        if typePart == 'maker':
            postOnly = True
            timeInForce = 'PO'
        if typePart == 'ioc':
            timeInForce = 'IOC'
        if typePart == 'fok':
            timeInForce = 'FOK'
        price = self.safe_string(order, 'price')
        costString = self.safe_string(order, 'cummulativeQuoteQty')
        amountString = None
        if rawType != 'buy_market':
            amountString = self.safe_string_2(order, 'origQty', 'amount')
        filledString = self.safe_string_2(order, 'executedQty', 'deal_amount')
        return self.safe_order({
            'id': id,
            'clientOrderId': clientOrderId,
            'datetime': self.iso8601(timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': None,
            'status': self.parse_order_status(rawStatus),
            'symbol': market['symbol'],
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': price,
            'stopPrice': None,
            'cost': costString,
            'amount': amountString,
            'filled': filledString,
            'remaining': None,
            'trades': None,
            'fee': None,
            'info': order,
            'average': None,
        }, market)

    async def fetch_order(self, id, symbol=None, params={}):
        """
        fetches information on an order made by the user
        :param str|None symbol: unified symbol of the market the order was made in
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        await self.load_markets()
        method = self.safe_string(params, 'method')
        if method is None:
            options = self.safe_value(self.options, 'fetchOrder', {})
            method = self.safe_string(options, 'method', 'fetchOrderSupplement')
        result = await getattr(self, method)(id, symbol, params)
        return result

    async def fetch_order_supplement(self, id, symbol=None, params={}):
        await self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'orderId': id,
        }
        response = await self.privatePostSupplementOrdersInfo(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data":{
        #              "cummulativeQuoteQty":0,
        #              "symbol":"doge_usdt",
        #              "executedQty":0,
        #              "orderId":"53d2d53e-70fb-4398-b722-f48571a5f61e",
        #              "origQty":1E+2,
        #              "price":0.05,
        #              "clientOrderId":null,
        #              "origQuoteOrderQty":5,
        #              "updateTime":1648163406000,
        #              "time":1648163139387,
        #              "type":"buy_maker",
        #              "status":-1
        #              },
        #          "error_code":0,
        #          "ts":1648164471827
        #      }
        #
        result = self.safe_value(response, 'data', {})
        return self.parse_order(result)

    async def fetch_order_default(self, id, symbol=None, params={}):
        # Id can be a list of ids delimited by a comma
        await self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'order_id': id,
        }
        response = await self.privatePostOrdersInfo(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data":[
        #              {
        #                  "symbol":"doge_usdt",
        #                  "amount":18,
        #                  "create_time":1647455223186,
        #                  "price":0,
        #                  "avg_price":0.113344,
        #                  "type":"sell_market",
        #                  "order_id":"d4ca1ddd-40d9-42c1-9717-5de435865bec",
        #                  "deal_amount":18,
        #                  "status":2
        #                }
        #            ],
        #          "error_code":0,
        #          "ts":1647455270776
        #      }
        #
        result = self.safe_value(response, 'data', [])
        numOrders = len(result)
        if numOrders == 1:
            return self.parse_order(result[0])
        else:
            parsedOrders = []
            for i in range(0, numOrders):
                parsedOrder = self.parse_order(result[i])
                parsedOrders.append(parsedOrder)
            return parsedOrders

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all trades made by the user
        :param str symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch trades for
        :param int|None limit: the maximum number of trades structures to retrieve
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        since = self.safe_value(params, 'start_date', since)
        params = self.omit(params, 'start_date')
        request = {
            'symbol': market['id'],
            # 'start_date': str Start time yyyy-mm-dd, the maximum is today, the default is yesterday
            # 'end_date': str Finish time yyyy-mm-dd, the maximum is today, the default is today
            # 'The start': and end date of the query window is up to 2 days
            # 'from': str Initial transaction number inquiring
            # 'direct': str inquire direction,The default is the 'next' which is the positive sequence of dealing time，the 'prev' is inverted order of dealing time
            # 'size': str Query the number of defaults to 100
        }
        if limit is not None:
            request['size'] = limit
        if since is not None:
            request['start_date'] = self.ymd(since, '-')  # max query 2 days ago
        response = await self.privatePostTransactionHistory(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data":[
        #              {
        #                  "orderUuid":"38b4e7a4-14f6-45fd-aba1-1a37024124a0",
        #                  "tradeFeeRate":0.0010000000,
        #                  "dealTime":1648500944496,
        #                  "dealQuantity":30.00000000000000000000,
        #                  "tradeFee":0.00453300000000000000,
        #                  "txUuid":"11f3850cc6214ea3b495adad3a032794",
        #                  "dealPrice":0.15111300000000000000,
        #                  "dealVolumePrice":4.53339000000000000000,
        #                  "tradeType":"sell_market"
        #              }
        #          ],
        #          "error_code":0,
        #          "ts":1648509742164
        #      }
        #
        trades = self.safe_value(response, 'data', [])
        return self.parse_trades(trades, market, since, limit)

    async def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        # default query is for canceled and completely filled orders
        # does not return open orders unless specified explicitly
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        if limit is None:
            limit = 100
        request = {
            'symbol': market['id'],
            'current_page': 1,
            'page_length': limit,
            # 'status'  -1: Cancelled, 0: Unfilled, 1: Partially filled, 2: Completely filled, 3: Partially filled and cancelled, 4: Cancellation is being processed
        }
        response = await self.privatePostSupplementOrdersInfoHistory(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data":{
        #              "total":1,
        #              "page_length":100,
        #              "orders":[
        #                  {
        #                      "cummulativeQuoteQty":0,
        #                      "symbol":"doge_usdt",
        #                      "executedQty":0,
        #                      "orderId":"2cadc7cc-b5f6-486b-a5b4-d6ac49a9c186",
        #                      "origQty":100,
        #                      "price":0.05,
        #                      "origQuoteOrderQty":5,
        #                      "updateTime":1648501384000,
        #                      "time":1648501363889,
        #                      "type":"buy",
        #                      "status":-1
        #                  }, ...
        #              ],
        #              "current_page":1
        #          },
        #          "error_code":0,
        #          "ts":1648505706348
        #      }
        #
        result = self.safe_value(response, 'data', {})
        orders = self.safe_value(result, 'orders', [])
        return self.parse_orders(orders, market, since, limit)

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all unfilled currently open orders
        :param str symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch open orders for
        :param int|None limit: the maximum number of  open orders structures to retrieve
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        if limit is None:
            limit = 100
        request = {
            'symbol': market['id'],
            'current_page': 1,
            'page_length': limit,
        }
        response = await self.privatePostSupplementOrdersInfoNoDeal(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data":{
        #              "total":1,
        #              "page_length":100,
        #              "orders":[
        #                  {
        #                      "cummulativeQuoteQty":0,
        #                      "symbol":"doge_usdt",
        #                      "executedQty":0,
        #                      "orderId":"73878edf-008d-4e4c-8041-df1f1b2cd8bb",
        #                      "origQty":100,
        #                      "price":0.05,
        #                      "origQuoteOrderQty":5,
        #                      "updateTime":1648501762000,
        #                      "time":1648501762353,
        #                      "type":"buy",
        #                      "status":0
        #                  }, ...
        #             ],
        #             "current_page":1
        #         },
        #         "error_code":0,
        #         "ts":1648506110196
        #     }
        #
        result = self.safe_value(response, 'data', {})
        orders = self.safe_value(result, 'orders', [])
        return self.parse_orders(orders, market, since, limit)

    async def cancel_order(self, id, symbol=None, params={}):
        """
        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 lbank2 api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        await self.load_markets()
        clientOrderId = self.safe_string_2(params, 'origClientOrderId', 'clientOrderId')
        params = self.omit(params, ['origClientOrderId', 'clientOrderId'])
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if clientOrderId is not None:
            request['origClientOrderId'] = clientOrderId
        request['orderId'] = id
        response = await self.privatePostSupplementCancelOrder(self.extend(request, params))
        #
        #   {
        #      "result":true,
        #      "data":{
        #          "executedQty":0.0,
        #          "price":0.05,
        #          "origQty":100.0,
        #          "tradeType":"buy",
        #          "status":0
        #          },
        #      "error_code":0,
        #      "ts":1648501286196
        #  }
        result = self.safe_value(response, 'data', {})
        return result

    async def cancel_all_orders(self, symbol=None, params={}):
        """
        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 lbank2 api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await self.privatePostSupplementCancelOrderBySymbol(self.extend(request, params))
        #
        #      {
        #          "result":"true",
        #          "data":[
        #              {
        #                  "executedQty":0.00000000000000000000,
        #                  "orderId":"293ef71b-3e67-4962-af93-aa06990a045f",
        #                  "price":0.05000000000000000000,
        #                  "origQty":100.00000000000000000000,
        #                  "tradeType":"buy",
        #                  "status":0
        #              },
        #          ],
        #          "error_code":0,
        #          "ts":1648506641469
        #      }
        #
        result = self.safe_value(response, 'data', [])
        return result

    def get_network_code_for_currency(self, currencyCode, params):
        defaultNetworks = self.safe_value(self.options, 'defaultNetworks')
        defaultNetwork = self.safe_string_upper(defaultNetworks, currencyCode)
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network', defaultNetwork)  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
        return network

    async def fetch_deposit_address(self, code, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :param str code: unified currency code
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/en/latest/manual.html#address-structure>`
        """
        await self.load_markets()
        method = self.safe_string(params, 'method')
        params = self.omit(params, 'method')
        if method is None:
            options = self.safe_value(self.options, 'fetchDepositAddress', {})
            method = self.safe_string(options, 'method', 'fetchPrivateTradingFees')
        return await getattr(self, method)(code, params)

    async def fetch_deposit_address_default(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'assetCode': currency['id'],
        }
        network = self.get_network_code_for_currency(code, params)
        if network is not None:
            request['netWork'] = network  # ... yes, really lol
            params = self.omit(params, 'network')
        response = await self.privatePostGetDepositAddress(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data":{
        #              "assetCode":"usdt",
        #              "address":"0xc85689d37ca650bf2f2161364cdedee21eb6ca53",
        #              "memo":null,
        #              "netWork":"bep20(bsc)"
        #              },
        #          "error_code":0,
        #          "ts":1648075865103
        #      }
        #
        result = self.safe_value(response, 'data')
        address = self.safe_string(result, 'address')
        tag = self.safe_string(result, 'memo')
        networkId = self.safe_string(result, 'netWork')
        inverseNetworks = self.safe_value(self.options, 'inverse-networks', {})
        networkCode = self.safe_string_upper(inverseNetworks, networkId, networkId)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': networkCode,
            'info': response,
        }

    async def fetch_deposit_address_supplement(self, code, params={}):
        # returns the address for whatever the default network is...
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
        }
        networks = self.safe_value(self.options, 'networks')
        network = self.safe_string_upper(params, 'network')
        network = self.safe_string(networks, network, network)
        if network is not None:
            request['networkName'] = network
            params = self.omit(params, 'network')
        response = await self.privatePostSupplementGetDepositAddress(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data":{
        #              "address":"TDxtabCC8iQwaxUUrPcE4WL2jArGAfvQ5A",
        #              "memo":null,
        #              "coin":"usdt"
        #              },
        #          "error_code":0,
        #          "ts":1648073818880
        #     }
        #
        result = self.safe_value(response, 'data')
        address = self.safe_string(result, 'address')
        tag = self.safe_string(result, 'memo')
        inverseNetworks = self.safe_value(self.options, 'inverse-networks', {})
        networkCode = self.safe_string_upper(inverseNetworks, network, network)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': networkCode,  # will be None if not specified in request
            'info': response,
        }

    async def withdraw(self, code, amount, address, tag=None, params={}):
        """
        make a withdrawal
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str|None tag:
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        await self.load_markets()
        fee = self.safe_string(params, 'fee')
        params = self.omit(params, 'fee')
        if fee is None:
            raise ArgumentsRequired(self.id + ' withdraw() requires a fee argument to be supplied in params, the relevant coin network fee can be found by calling fetchTransactionFees(), note: if no network param is supplied then the default network will be used, self can also be found in fetchTransactionFees()')
        currency = self.currency(code)
        request = {
            'address': address,
            'coin': currency['id'],
            'amount': amount,
            'fee': fee,  # the correct coin-network fee must be supplied, which can be found by calling fetchTransactionFees(private)
            # 'networkName': defaults to the defaultNetwork of the coin which can be found in the /supplement/user_info endpoint
            # 'memo': memo: memo word of bts and dct
            # 'mark': Withdrawal Notes
            # 'name': Remarks of the address. After filling in self parameter, it will be added to the withdrawal address book of the currency.
            # 'withdrawOrderId': withdrawOrderId
            # 'type': type=1 is for intra-site transfer
        }
        if tag is not None:
            request['memo'] = tag
        network = self.safe_string_upper_2(params, 'network', 'networkName')
        params = self.omit(params, ['network', 'networkName'])
        networks = self.safe_value(self.options, 'networks')
        networkId = self.safe_string(networks, network, network)
        if networkId is not None:
            request['networkName'] = networkId
        response = await self.privatePostSupplementWithdraw(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data": {
        #              "fee":10.00000000000000000000,
        #              "withdrawId":1900376
        #              },
        #          "error_code":0,
        #          "ts":1648992501414
        #      }
        #
        result = self.safe_value(response, 'data', {})
        return {
            'info': result,
            'id': self.safe_string(result, 'withdrawId'),
        }

    def parse_transaction_status(self, status, type):
        statuses = {
            'deposit': {
                '1': 'pending',
                '2': 'ok',
                '3': 'failed',
                '4': 'canceled',
                '5': 'transfer',
            },
            'withdrawal': {
                '1': 'pending',
                '2': 'canceled',
                '3': 'failed',
                '4': 'ok',
            },
        }
        return self.safe_string(self.safe_value(statuses, type, {}), status, status)

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchDeposits(private)
        #
        #      {
        #          "insertTime":1649012310000,
        #          "amount":9.00000000000000000000,
        #          "address":"TYASr5UV6HEcXatwdFQfmLVUqQQQMUxHLS",
        #          "networkName":"trc20",
        #          "txId":"081e4e9351dd0274922168da5f2d14ea6c495b1c3b440244f4a6dd9fe196bf2b",
        #          "coin":"usdt",
        #          "status":"2"
        #      }
        #
        #
        # fetchWithdrawals(private)
        #
        #      {
        #          "amount":2.00000000000000000000,
        #          "address":"TBjrW5JHDyPZjFc5nrRMhRWUDaJmhGhmD6",
        #          "fee":1.00000000000000000000,
        #          "networkName":"trc20",
        #          "coid":"usdt",
        #          "transferType":"数字资产提现",
        #          "txId":"47eeee2763ad49b8817524dacfa7d092fb58f8b0ab7e5d25473314df1a793c3d",
        #          "id":1902194,
        #          "applyTime":1649014002000,
        #          "status":"4"
        #      }
        #
        id = self.safe_string(transaction, 'id')
        type = None
        if id is None:
            type = 'deposit'
        else:
            type = 'withdrawal'
        txid = self.safe_string(transaction, 'txId')
        timestamp = self.safe_integer_2(transaction, 'insertTime', 'applyTime')
        networks = self.safe_value(self.options, 'inverse-networks', {})
        networkId = self.safe_string(transaction, 'networkName')
        network = self.safe_string(networks, networkId, networkId)
        address = self.safe_string(transaction, 'address')
        addressFrom = None
        addressTo = None
        if type == 'deposit':
            addressFrom = address
        else:
            addressTo = address
        amount = self.safe_number(transaction, 'amount')
        currencyId = self.safe_string_2(transaction, 'coin', 'coid')
        code = self.safe_currency_code(currencyId, currency)
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'), type)
        fee = None
        feeCost = self.safe_number(transaction, 'fee')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': code,
            }
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': network,
            'address': address,
            'addressTo': addressTo,
            'addressFrom': addressFrom,
            'tag': None,
            'tagTo': None,
            'tagFrom': None,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': None,
            'comment': None,
            'internal': (status == 'transfer'),
            'fee': fee,
        }

    async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        """
        fetch all deposits made to an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch deposits for
        :param int|None limit: the maximum number of deposits structures to retrieve
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        await self.load_markets()
        request = {
            # 'status': Recharge status: ("1","Applying"),("2","Recharge successful"),("3","Recharge failed"),("4","Already Cancel"),("5", "Transfer")
            # 'endTime': end time, timestamp in milliseconds, default now
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        if since is not None:
            request['startTime'] = since
        response = await self.privatePostSupplementDepositHistory(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data": {
        #              "total":1,
        #              "depositOrders": [
        #                  {
        #                      "insertTime":1649012310000,
        #                      "amount":9.00000000000000000000,
        #                      "address":"TYASr5UV6HEcXatwdFQfmLVUqQQQMUxHLS",
        #                      "networkName":"trc20",
        #                      "txId":"081e4e9351dd0274922168da5f2d14ea6c495b1c3b440244f4a6dd9fe196bf2b",
        #                      "coin":"usdt",
        #                      "status":"2"
        #                  },
        #              ],
        #              "page_length":20,
        #              "current_page":1
        #          },
        #          "error_code":0,
        #          "ts":1649719721758
        #      }
        #
        data = self.safe_value(response, 'data', {})
        deposits = self.safe_value(data, 'depositOrders', [])
        return self.parse_transactions(deposits, currency, since, limit)

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        """
        fetch all withdrawals made from an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch withdrawals for
        :param int|None limit: the maximum number of withdrawals structures to retrieve
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        await self.load_markets()
        request = {
            # 'status': Recharge status: ("1","Applying"),("2","Recharge successful"),("3","Recharge failed"),("4","Already Cancel"),("5", "Transfer")
            # 'endTime': end time, timestamp in milliseconds, default now
            # 'withdrawOrderId': Custom withdrawal id
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        if since is not None:
            request['startTime'] = since
        response = await self.privatePostSupplementWithdraws(self.extend(request, params))
        #
        #      {
        #          "result":true,
        #          "data": {
        #              "total":1,
        #              "withdraws": [
        #                  {
        #                      "amount":2.00000000000000000000,
        #                      "address":"TBjrW5JHDyPZjFc5nrRMhRWUDaJmhGhmD6",
        #                      "fee":1.00000000000000000000,
        #                      "networkName":"trc20",
        #                      "coid":"usdt",
        #                      "transferType":"数字资产提现",
        #                      "txId":"47eeee2763ad49b8817524dacfa7d092fb58f8b0ab7e5d25473314df1a793c3d",
        #                      "id":1902194,
        #                      "applyTime":1649014002000,
        #                      "status":"4"
        #                  },
        #              ],
        #              "page_length":20,
        #              "current_page":1
        #          },
        #          "error_code":0,
        #          "ts":1649720362362
        #      }
        #
        data = self.safe_value(response, 'data', {})
        withdraws = self.safe_value(data, 'withdraws', [])
        return self.parse_transactions(withdraws, currency, since, limit)

    async def fetch_transaction_fees(self, codes=None, params={}):
        """
        fetch transaction fees
        :param [str]|None codes: not used by lbank2 fetchTransactionFees()
        :param dict params: extra parameters specific to the lbank2 api endpoint
        :returns dict: a list of `fee structures <https://docs.ccxt.com/en/latest/manual.html#fee-structure>`
        """
        # private only returns information for currencies with non-zero balance
        await self.load_markets()
        isAuthorized = self.check_required_credentials(False)
        result = None
        if isAuthorized is True:
            method = self.safe_string(params, 'method')
            params = self.omit(params, 'method')
            if method is None:
                options = self.safe_value(self.options, 'fetchTransactionFees', {})
                method = self.safe_string(options, 'method', 'fetchPrivateTransactionFees')
            result = await getattr(self, method)(params)
        else:
            result = await self.fetch_public_transaction_fees(params)
        return result

    async def fetch_private_transaction_fees(self, params={}):
        # complete response
        # incl. for coins which None in public method
        await self.load_markets()
        response = await self.privatePostSupplementUserInfo()
        #
        #    {
        #        "result": "true",
        #        "data": [
        #            {
        #                "usableAmt": "14.36",
        #                "assetAmt": "14.36",
        #                "networkList": [
        #                    {
        #                        "isDefault": False,
        #                        "withdrawFeeRate": "",
        #                        "name": "erc20",
        #                        "withdrawMin": 30,
        #                        "minLimit": 0.0001,
        #                        "minDeposit": 20,
        #                        "feeAssetCode": "usdt",
        #                        "withdrawFee": "30",
        #                        "type": 1,
        #                        "coin": "usdt",
        #                        "network": "eth"
        #                    },
        #                    ...
        #                ],
        #                "freezeAmt": "0",
        #                "coin": "ada"
        #            }
        #        ],
        #        "code": 0
        #    }
        #
        result = self.safe_value(response, 'data', [])
        withdrawFees = {}
        for i in range(0, len(result)):
            entry = result[i]
            currencyId = self.safe_string(entry, 'coin')
            code = self.safe_currency_code(currencyId)
            networkList = self.safe_value(entry, 'networkList', [])
            withdrawFees[code] = {}
            for j in range(0, len(networkList)):
                networkEntry = networkList[j]
                networkId = self.safe_string(networkEntry, 'name')
                networkCode = self.safe_string(self.options['inverse-networks'], networkId, networkId)
                fee = self.safe_number(networkEntry, 'withdrawFee')
                if fee is not None:
                    withdrawFees[code][networkCode] = fee
        return {
            'withdraw': withdrawFees,
            'deposit': {},
            'info': response,
        }

    async def fetch_public_transaction_fees(self, params={}):
        # extremely incomplete response
        # vast majority fees None
        await self.load_markets()
        code = self.safe_string_2(params, 'coin', 'assetCode')
        params = self.omit(params, ['coin', 'assetCode'])
        request = {}
        if code is not None:
            currency = self.currency(code)
            request['assetCode'] = currency['id']
        response = await self.publicGetWithdrawConfigs(self.extend(request, params))
        #
        #    {
        #        result: 'true',
        #        data: [
        #          {
        #            amountScale: '4',
        #            chain: 'heco',
        #            assetCode: 'lbk',
        #            min: '200',
        #            transferAmtScale: '4',
        #            canWithDraw: True,
        #            fee: '100',
        #            minTransfer: '0.0001',
        #            type: '1'
        #          },
        #          ...
        #        ],
        #        error_code: '0',
        #        ts: '1663364435973'
        #    }
        #
        result = self.safe_value(response, 'data', [])
        withdrawFees = {}
        for i in range(0, len(result)):
            item = result[i]
            canWithdraw = self.safe_string(item, 'canWithDraw')
            if canWithdraw == 'true':
                currencyId = self.safe_string(item, 'assetCode')
                code = self.safe_currency_code(currencyId)
                chain = self.safe_string(item, 'chain')
                network = self.safe_string(self.options['inverse-networks'], chain, chain)
                if network is None:
                    network = code
                fee = self.safe_string(item, 'fee')
                if withdrawFees[code] is None:
                    withdrawFees[code] = {}
                withdrawFees[code][network] = self.parse_number(fee)
        return {
            'withdraw': withdrawFees,
            'deposit': {},
            'info': response,
        }

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        query = self.omit(params, self.extract_params(path))
        url = self.urls['api']['rest'] + '/' + self.version + '/' + self.implode_params(path, params)
        # Every endpoint ends with ".do"
        url += '.do'
        if api == 'public':
            if query:
                url += '?' + self.urlencode(self.keysort(query))
        else:
            self.check_required_credentials()
            timestamp = str(self.milliseconds())
            echostr = self.uuid22() + self.uuid16()
            query = self.extend({
                'api_key': self.apiKey,
            }, query)
            signatureMethod = None
            if len(self.secret) > 32:
                signatureMethod = 'RSA'
            else:
                signatureMethod = 'HmacSHA256'
            auth = self.rawencode(self.keysort(self.extend({
                'echostr': echostr,
                'signature_method': signatureMethod,
                'timestamp': timestamp,
            }, query)))
            encoded = self.encode(auth)
            hash = self.hash(encoded)
            uppercaseHash = hash.upper()
            sign = None
            if signatureMethod == 'RSA':
                cacheSecretAsPem = self.safe_value(self.options, 'cacheSecretAsPem', True)
                pem = None
                if cacheSecretAsPem:
                    pem = self.safe_value(self.options, 'pem')
                    if pem is None:
                        pem = self.convert_secret_to_pem(self.encode(self.secret))
                        self.options['pem'] = pem
                else:
                    pem = self.convert_secret_to_pem(self.encode(self.secret))
                encodedPem = self.encode(pem)
                sign = self.binary_to_base64(self.rsa(uppercaseHash, encodedPem, 'RS256'))
            elif signatureMethod == 'HmacSHA256':
                sign = self.hmac(self.encode(uppercaseHash), self.encode(self.secret))
            query['sign'] = sign
            body = self.urlencode(self.keysort(query))
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
                'timestamp': timestamp,
                'signature_method': signatureMethod,
                'echostr': echostr,
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def convert_secret_to_pem(self, secret):
        lineLength = 64
        secretLength = len(secret) - 0
        numLines = int(secretLength / lineLength)
        numLines = self.sum(numLines, 1)
        pem = "-----BEGIN PRIVATE KEY-----\n"  # eslint-disable-line
        for i in range(0, numLines):
            start = i * lineLength
            end = self.sum(start, lineLength)
            pem += self.secret[start:end] + "\n"  # eslint-disable-line
        return pem + '-----END PRIVATE KEY-----'

    def handle_errors(self, httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return
        success = self.safe_string(response, 'result')
        if success == 'false':
            errorCode = self.safe_string(response, 'error_code')
            message = self.safe_string({
                '10000': 'Internal error',
                '10001': 'The required parameters can not be empty',
                '10002': 'Validation failed',
                '10003': 'Invalid parameter',
                '10004': 'Request too frequent',
                '10005': 'Secret key does not exist',
                '10006': 'User does not exist',
                '10007': 'Invalid signature',
                '10008': 'Invalid Trading Pair',
                '10009': 'Price and/or Amount are required for limit order',
                '10010': 'Price and/or Amount must be less than minimum requirement',
                # '10011': 'Market orders can not be missing the amount of the order',
                # '10012': 'market sell orders can not be missing orders',
                '10013': 'The amount is too small',
                '10014': 'Insufficient amount of money in the account',
                '10015': 'Invalid order type',
                '10016': 'Insufficient account balance',
                '10017': 'Server Error',
                '10018': 'Page size should be between 1 and 50',
                '10019': 'Cancel NO more than 3 orders in one request',
                '10020': 'Volume < 0.001',
                '10021': 'Price < 0.01',
                '10022': 'Invalid authorization',
                '10023': 'Market Order is not supported yet',
                '10024': 'User cannot trade on self pair',
                '10025': 'Order has been filled',
                '10026': 'Order has been cancelld',
                '10027': 'Order is cancelling',
                '10028': 'Wrong query time',
                '10029': 'from is not in the query time',
                '10030': 'from do not match the transaction type of inqury',
                '10031': 'echostr length must be valid and length must be from 30 to 40',
                '10033': 'Failed to create order',
                '10036': 'customID duplicated',
                '10100': 'Has no privilege to withdraw',
                '10101': 'Invalid fee rate to withdraw',
                '10102': 'Too little to withdraw',
                '10103': 'Exceed daily limitation of withdraw',
                '10104': 'Cancel was rejected',
                '10105': 'Request has been cancelled',
                '10106': 'None trade time',
                '10107': 'Start price exception',
                '10108': 'can not create order',
                '10109': 'wallet address is not mapping',
                '10110': 'transfer fee is not mapping',
                '10111': 'mount > 0',
                '10112': 'fee is too lower',
                '10113': 'transfer fee is 0',
                '10600': 'intercepted by replay attacks filter, check timestamp',
                '10601': 'Interface closed unavailable',
                '10701': 'invalid asset code',
                '10702': 'not allowed deposit',
            }, errorCode, self.json(response))
            ErrorClass = self.safe_value({
                '10001': BadRequest,
                '10002': AuthenticationError,
                '10003': BadRequest,
                '10004': RateLimitExceeded,
                '10005': AuthenticationError,
                '10006': AuthenticationError,
                '10007': AuthenticationError,
                '10008': BadSymbol,
                '10009': InvalidOrder,
                '10010': InvalidOrder,
                '10013': InvalidOrder,
                '10014': InsufficientFunds,
                '10015': InvalidOrder,
                '10016': InsufficientFunds,
                '10017': ExchangeError,
                '10018': BadRequest,
                '10019': BadRequest,
                '10020': BadRequest,
                '10021': InvalidOrder,
                '10022': PermissionDenied,  # 'Invalid authorization',
                '10023': InvalidOrder,  # 'Market Order is not supported yet',
                '10024': PermissionDenied,  # 'User cannot trade on self pair',
                '10025': InvalidOrder,  # 'Order has been filled',
                '10026': InvalidOrder,  # 'Order has been cancelled',
                '10027': InvalidOrder,  # 'Order is cancelling',
                '10028': BadRequest,  # 'Wrong query time',
                '10029': BadRequest,  # 'from is not in the query time',
                '10030': BadRequest,  # 'from do not match the transaction type of inqury',
                '10031': InvalidNonce,  # 'echostr length must be valid and length must be from 30 to 40',
                '10033': ExchangeError,  # 'Failed to create order',
                '10036': DuplicateOrderId,  # 'customID duplicated',
                '10100': PermissionDenied,  # 'Has no privilege to withdraw',
                '10101': BadRequest,  # 'Invalid fee rate to withdraw',
                '10102': InsufficientFunds,  # 'Too little to withdraw',
                '10103': ExchangeError,  # 'Exceed daily limitation of withdraw',
                '10104': ExchangeError,  # 'Cancel was rejected',
                '10105': ExchangeError,  # 'Request has been cancelled',
                '10106': BadRequest,  # 'None trade time',
                '10107': BadRequest,  # 'Start price exception',
                '10108': ExchangeError,  # 'can not create order',
                '10109': InvalidAddress,  # 'wallet address is not mapping',
                '10110': ExchangeError,  # 'transfer fee is not mapping',
                '10111': BadRequest,  # 'mount > 0',
                '10112': BadRequest,  # 'fee is too lower',
                '10113': BadRequest,  # 'transfer fee is 0',
                '10600': BadRequest,  # 'intercepted by replay attacks filter, check timestamp',
                '10601': ExchangeError,  # 'Interface closed unavailable',
                '10701': BadSymbol,  # 'invalid asset code',
                '10702': PermissionDenied,  # 'not allowed deposit',
            }, errorCode, ExchangeError)
            raise ErrorClass(message)
