UNPKG

ccxt-xigua

Version:

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

2,157 lines 92.3 kB
'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError, InvalidAddress, DuplicateOrderId, ArgumentsRequired, InsufficientFunds, InvalidOrder, InvalidNonce, AuthenticationError, RateLimitExceeded, PermissionDenied, BadRequest, BadSymbol } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');

//  ---------------------------------------------------------------------------

module.exports = class lbank2 extends Exchange {
    describe () {
        return this.deepExtend (super.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': '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': this.parseNumber ('0.001'),
                    'taker': this.parseNumber ('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': 'fetchPrivateFundingFees', // or 'fetchPublicFundingFees'
                },
                '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',
                },
            },
        });
    }

    async fetchMarkets (params = {}) {
        /**
         * @method
         * @name lbank2#fetchMarkets
         * @description 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
        const response = await this.publicGetAccuracy ();
        const data = this.safeValue (response, 'data');
        //      [
        //          {
        //              symbol: 'snx3s_usdt',
        //              quantityAccuracy: '2',
        //              minTranQua: '0.01',
        //              priceAccuracy: '6'
        //          }
        //     ]
        const result = [];
        for (let i = 0; i < data.length; i++) {
            const market = data[i];
            const marketId = this.safeString (market, 'symbol');
            const parts = marketId.split ('_');
            const baseId = parts[0];
            const quoteId = parts[1];
            const base = baseId.toUpperCase ();
            const quote = quoteId.toUpperCase ();
            let symbol = base + '/' + quote;
            const productTypes = {
                '3l': true,
                '5l': true,
                '3s': true,
                '5s': true,
            };
            const ending = baseId.slice (-2);
            const isLeveragedProduct = this.safeValue (productTypes, ending, false);
            if (isLeveragedProduct) {
                symbol += ':' + quote;
            }
            let linear = undefined;
            if (isLeveragedProduct === true) {
                linear = true;
            }
            result.push ({
                'id': marketId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': undefined,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': undefined,
                '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': undefined,
                'contractSize': undefined,
                'expiry': undefined,
                'expiryDatetime': undefined,
                'strike': undefined,
                'optionType': undefined,
                'precision': {
                    'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'quantityAccuracy'))),
                    'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'priceAccuracy'))),
                },
                'limits': {
                    'leverage': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'amount': {
                        'min': this.safeNumber (market, 'minTranQua'),
                        'max': undefined,
                    },
                    'price': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'cost': {
                        'min': undefined,
                        'max': undefined,
                    },
                },
                'info': market,
            });
        }
        return result;
    }

    parseTicker (ticker, market = undefined) {
        //
        //      {
        //          "symbol":"btc_usdt",
        //          "ticker": {
        //              "high":40200.88,
        //              "vol":7508.3096,
        //              "low":38239.38,
        //              "change":0.75,
        //              "turnover":292962771.34,
        //              "latest":39577.95
        //               },
        //           "timestamp":1647005189792
        //      }
        //
        const marketId = this.safeString (ticker, 'symbol');
        const symbol = this.safeSymbol (marketId, market);
        const timestamp = this.safeInteger (ticker, 'timestamp');
        const tickerData = this.safeValue (ticker, 'ticker');
        return this.safeTicker ({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeString (tickerData, 'high'),
            'low': this.safeString (tickerData, 'low'),
            'bid': undefined,
            'bidVolume': undefined,
            'ask': undefined,
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'last': this.safeString (tickerData, 'latest'),
            'previousClose': undefined,
            'change': undefined,
            'percentage': this.safeString (tickerData, 'change'),
            'average': undefined,
            'baseVolume': this.safeString (tickerData, 'vol'),
            'quoteVolume': this.safeString (tickerData, 'turnover'),
            'info': ticker,
        }, market);
    }

    async fetchTicker (symbol, params = {}) {
        /**
         * @method
         * @name lbank2#fetchTicker
         * @description 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]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
         */
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        const response = await this.publicGetTicker24hr (this.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
        //      }
        //
        const data = this.safeValue (response, 'data', []);
        const first = this.safeValue (data, 0, {});
        return this.parseTicker (first, market);
    }

    async fetchTickers (symbols = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchTickers
         * @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
         * @param {[str]|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
         */
        await this.loadMarkets ();
        const request = {
            'symbol': 'all',
        };
        const response = await this.publicGetTicker24hr (this.extend (request, params));
        const data = this.safeValue (response, 'data', []);
        return this.parseTickers (data, symbols);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchOrderBook
         * @description 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|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symbols
         */
        await this.loadMarkets ();
        const market = this.market (symbol);
        if (limit === undefined) {
            limit = 60;
        }
        const request = {
            'symbol': market['id'],
            'size': limit,
        };
        const response = await this.publicGetDepth (this.extend (request, params));
        const orderbook = response['data'];
        const timestamp = this.milliseconds ();
        return this.parseOrderBook (orderbook, market['symbol'], timestamp);
    }

    parseTrade (trade, market = undefined) {
        //
        // 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"
        //      }
        //
        let timestamp = this.safeInteger2 (trade, 'date_ms', 'time');
        if (timestamp === undefined) {
            timestamp = this.safeInteger (trade, 'dealTime');
        }
        let amountString = this.safeString2 (trade, 'amount', 'qty');
        if (amountString === undefined) {
            amountString = this.safeString (trade, 'dealQuantity');
        }
        let priceString = this.safeString (trade, 'price');
        if (priceString === undefined) {
            priceString = this.safeString (trade, 'dealPrice');
        }
        let costString = this.safeString (trade, 'quoteQty');
        if (costString === undefined) {
            costString = this.safeString (trade, 'dealVolumePrice');
        }
        let side = this.safeString2 (trade, 'tradeType', 'type');
        let type = undefined;
        let takerOrMaker = undefined;
        if (side !== undefined) {
            const parts = side.split ('_');
            side = this.safeString (parts, 0);
            const typePart = this.safeString (parts, 1);
            type = 'limit';
            takerOrMaker = 'taker';
            if (typePart !== undefined) {
                if (typePart === 'market') {
                    type = 'market';
                } else if (typePart === 'maker') {
                    takerOrMaker = 'maker';
                }
            }
        }
        let id = this.safeString2 (trade, 'tid', 'id');
        if (id === undefined) {
            id = this.safeString (trade, 'txUuid');
        }
        const order = this.safeString (trade, 'orderUuid');
        const symbol = this.safeSymbol (undefined, market);
        let fee = undefined;
        const feeCost = this.safeString (trade, 'tradeFee');
        if (feeCost !== undefined) {
            fee = {
                'cost': feeCost,
                'currency': undefined,
                'rate': this.safeString (trade, 'tradeFeeRate'),
            };
        }
        return this.safeTrade ({
            'timestamp': timestamp,
            'datetime': this.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 fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchTrades
         * @description 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|undefined} since timestamp in ms of the earliest trade to fetch
         * @param {int|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades}
         */
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        if (since !== undefined) {
            request['time'] = since;
        }
        if (limit !== undefined) {
            request['size'] = limit;
        } else {
            request['size'] = 600; // max
        }
        let method = this.safeString (params, 'method');
        params = this.omit (params, 'method');
        if (method === undefined) {
            const options = this.safeValue (this.options, 'fetchTrades', {});
            method = this.safeString (options, 'method', 'publicGetTrades');
        }
        const response = await this[method] (this.extend (request, params));
        //
        //      {
        //          "result":"true",
        //          "data": [
        //              {
        //                  "date_ms":1647021989789,
        //                  "amount":0.0028,
        //                  "price":38804.2,
        //                  "type":"buy",
        //                  "tid":"52d5616ee35c43019edddebe59b3e094"
        //               }
        //           ],
        //           "error_code":0,
        //           "ts":1647021999308
        //      }
        const trades = this.safeValue (response, 'data', []);
        return this.parseTrades (trades, market, since, limit);
    }

    parseOHLCV (ohlcv, market = undefined) {
        //
        //   [
        //     1482311500, // timestamp
        //     5423.23,    // open
        //     5472.80,    // high
        //     5516.09,    // low
        //     5462,       // close
        //     234.3250    // volume
        //   ],
        //
        return [
            this.safeTimestamp (ohlcv, 0), // timestamp
            this.safeNumber (ohlcv, 1), // open
            this.safeNumber (ohlcv, 2), // high
            this.safeNumber (ohlcv, 3), // low
            this.safeNumber (ohlcv, 4), // close
            this.safeNumber (ohlcv, 5), // volume
        ];
    }

    async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchOHLCV
         * @description 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|undefined} since timestamp in ms of the earliest candle to fetch
         * @param {int|undefined} 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 this.loadMarkets ();
        const market = this.market (symbol);
        if (since === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchOHLCV () requires a since argument');
        }
        if (limit === undefined) {
            limit = 100;
        }
        const request = {
            'symbol': market['id'],
            'type': this.timeframes[timeframe],
            'time': parseInt (since / 1000),
            'size': limit, // max 2000
        };
        const response = await this.publicGetKline (this.extend (request, params));
        const ohlcvs = this.safeValue (response, 'data', []);
        //
        //
        // [
        //   [
        //     1482311500,
        //     5423.23,
        //     5472.80,
        //     5516.09,
        //     5462,
        //     234.3250
        //   ],
        //   [
        //     1482311400,
        //     5432.52,
        //     5459.87,
        //     5414.30,
        //     5428.23,
        //     213.7329
        //   ]
        // ]
        //
        return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit);
    }

    parseBalance (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"
        //          }, ...
        //      ]
        //
        const timestamp = this.safeInteger (response, 'ts');
        const result = {
            'info': response,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
        };
        const data = this.safeValue (response, 'data');
        // from privatePostUserInfo
        const toBtc = this.safeValue (data, 'toBtc');
        if (toBtc !== undefined) {
            const used = this.safeValue (data, 'freeze', {});
            const free = this.safeValue (data, 'free', {});
            const currencies = Object.keys (free);
            for (let i = 0; i < currencies.length; i++) {
                const currencyId = currencies[i];
                const code = this.safeCurrencyCode (currencyId);
                const account = this.account ();
                account['used'] = this.safeString (used, currencyId);
                account['free'] = this.safeString (free, currencyId);
                result[code] = account;
            }
            return this.safeBalance (result);
        }
        // from privatePostSupplementUserInfoAccount
        const balances = this.safeValue (data, 'balances');
        if (balances !== undefined) {
            for (let i = 0; i < balances.length; i++) {
                const item = balances[i];
                const currencyId = this.safeString (item, 'asset');
                const code = this.safeCurrencyCode (currencyId);
                const account = this.account ();
                account['free'] = this.safeString (item, 'free');
                account['used'] = this.safeString (item, 'locked');
                result[code] = account;
            }
            return this.safeBalance (result);
        }
        // from privatePostSupplementUserInfo
        const isArray = Array.isArray (data);
        if (isArray === true) {
            for (let i = 0; i < data.length; i++) {
                const item = data[i];
                const currencyId = this.safeString (item, 'coin');
                const code = this.safeCurrencyCode (currencyId);
                const account = this.account ();
                account['free'] = this.safeString (item, 'usableAmt');
                account['used'] = this.safeString (item, 'freezeAmt');
                result[code] = account;
            }
            return this.safeBalance (result);
        }
    }

    async fetchBalance (params = {}) {
        /**
         * @method
         * @name lbank2#fetchBalance
         * @description 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]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
         */
        await this.loadMarkets ();
        let method = this.safeString (params, 'method');
        if (method === undefined) {
            const options = this.safeValue (this.options, 'fetchBalance', {});
            method = this.safeString (options, 'method', 'privatePostSupplementUserInfo');
        }
        const response = await this[method] ();
        return this.parseBalance (response);
    }

    parseTradingFee (fee, market = undefined) {
        //
        //      {
        //          "symbol":"skt_usdt",
        //          "makerCommission":"0.10",
        //          "takerCommission":"0.10"
        //      }
        //
        const marketId = this.safeString (fee, 'symbol');
        const symbol = this.safeSymbol (marketId);
        return {
            'info': fee,
            'symbol': symbol,
            'maker': this.safeNumber (fee, 'makerCommission'),
            'taker': this.safeNumber (fee, 'takerCommission'),
        };
    }

    async fetchTradingFee (symbol, params = {}) {
        /**
         * @method
         * @name lbank2#fetchTradingFee
         * @description 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]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure}
         */
        const market = this.market (symbol);
        const result = await this.fetchTradingFees (this.extend (params, { 'category': market['id'] }));
        return result;
    }

    async fetchTradingFees (params = {}) {
        /**
         * @method
         * @name lbank2#fetchTradingFees
         * @description 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]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure} indexed by market symbols
         */
        await this.loadMarkets ();
        const request = {};
        const response = await this.privatePostSupplementCustomerTradeFee (this.extend (request, params));
        const fees = this.safeValue (response, 'data', []);
        const result = {};
        for (let i = 0; i < fees.length; i++) {
            const fee = this.parseTradingFee (fees[i]);
            const symbol = fee['symbol'];
            result[symbol] = fee;
        }
        return result;
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#createOrder
         * @description 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|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
         */
        await this.loadMarkets ();
        const market = this.market (symbol);
        const clientOrderId = this.safeString2 (params, 'custom_id', 'clientOrderId');
        const postOnly = this.safeValue (params, 'postOnly', false);
        const timeInForce = this.safeStringUpper (params, 'timeInForce');
        params = this.omit (params, [ 'custom_id', 'clientOrderId', 'timeInForce', 'postOnly' ]);
        const request = {
            'symbol': market['id'],
        };
        const ioc = (timeInForce === 'IOC');
        const fok = (timeInForce === 'FOK');
        const maker = (postOnly || (timeInForce === 'PO'));
        if ((type === 'market') && (ioc || fok || maker)) {
            throw new InvalidOrder (this.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'] = this.priceToPrecision (symbol, price);
            request['amount'] = this.amountToPrecision (symbol, amount);
            if (ioc) {
                request['type'] = side + '_' + 'ioc';
            } else if (fok) {
                request['type'] = side + '_' + 'fok';
            } else if (maker) {
                request['type'] = side + '_' + 'maker';
            }
        } else if (type === 'market') {
            if (side === 'sell') {
                request['type'] = side + '_' + 'market';
                request['amount'] = this.amountToPrecision (symbol, amount);
            } else if (side === 'buy') {
                request['type'] = side + '_' + 'market';
                if (this.options['createMarketBuyOrderRequiresPrice']) {
                    if (price === undefined) {
                        throw new InvalidOrder (this.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 {
                        const cost = parseFloat (amount) * parseFloat (price);
                        request['price'] = this.priceToPrecision (symbol, cost);
                    }
                } else {
                    request['price'] = amount;
                }
            }
        }
        if (clientOrderId !== undefined) {
            request['custom_id'] = clientOrderId;
        }
        let method = undefined;
        method = this.safeString (params, 'method');
        params = this.omit (params, 'method');
        if (method === undefined) {
            const options = this.safeValue (this.options, 'createOrder', {});
            method = this.safeString (options, 'method', 'privatePostSupplementCreateOrder');
        }
        const response = await this[method] (this.extend (request, params));
        //
        //      {
        //          "result":true,
        //          "data":{
        //              "symbol":"doge_usdt",
        //              "order_id":"0cf8a3de-4597-4296-af45-be7abaa06b07"
        //              },
        //          "error_code":0,
        //          "ts":1648162321043
        //      }
        //
        const result = this.safeValue (response, 'data', {});
        return {
            'id': this.safeString (result, 'order_id'),
            'info': result,
        };
    }

    parseOrderStatus (status) {
        const 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 this.safeString (statuses, status, status);
    }

    parseOrder (order, market = undefined) {
        //
        // 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
        //      }
        //
        const id = this.safeString2 (order, 'orderId', 'order_id');
        const clientOrderId = this.safeString2 (order, 'clientOrderId', 'custom_id');
        const timestamp = this.safeInteger2 (order, 'time', 'create_time');
        const rawStatus = this.safeString (order, 'status');
        const marketId = this.safeString (order, 'symbol');
        market = this.safeMarket (marketId, market);
        let timeInForce = undefined;
        let postOnly = false;
        let type = 'limit';
        const rawType = this.safeString (order, 'type'); // buy, sell, buy_market, sell_market, buy_maker,sell_maker,buy_ioc,sell_ioc, buy_fok, sell_fok
        const parts = rawType.split ('_');
        const side = this.safeString (parts, 0);
        const typePart = this.safeString (parts, 1); // market, maker, ioc, fok or undefined (limit)
        if (typePart === 'market') {
            type = 'market';
        }
        if (typePart === 'maker') {
            postOnly = true;
            timeInForce = 'PO';
        }
        if (typePart === 'ioc') {
            timeInForce = 'IOC';
        }
        if (typePart === 'fok') {
            timeInForce = 'FOK';
        }
        const price = this.safeString (order, 'price');
        const costString = this.safeString (order, 'cummulativeQuoteQty');
        let amountString = undefined;
        if (rawType !== 'buy_market') {
            amountString = this.safeString2 (order, 'origQty', 'amount');
        }
        const filledString = this.safeString2 (order, 'executedQty', 'deal_amount');
        return this.safeOrder ({
            'id': id,
            'clientOrderId': clientOrderId,
            'datetime': this.iso8601 (timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': undefined,
            'status': this.parseOrderStatus (rawStatus),
            'symbol': market['symbol'],
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': price,
            'stopPrice': undefined,
            'cost': costString,
            'amount': amountString,
            'filled': filledString,
            'remaining': undefined,
            'trades': undefined,
            'fee': undefined,
            'info': order,
            'average': undefined,
        }, market);
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchOrder
         * @description fetches information on an order made by the user
         * @param {str|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
         */
        await this.loadMarkets ();
        let method = this.safeString (params, 'method');
        if (method === undefined) {
            const options = this.safeValue (this.options, 'fetchOrder', {});
            method = this.safeString (options, 'method', 'fetchOrderSupplement');
        }
        const result = await this[method] (id, symbol, params);
        return result;
    }

    async fetchOrderSupplement (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchOrder () requires a symbol argument');
        }
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
            'orderId': id,
        };
        const response = await this.privatePostSupplementOrdersInfo (this.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
        //      }
        //
        const result = this.safeValue (response, 'data', {});
        return this.parseOrder (result);
    }

    async fetchOrderDefault (id, symbol = undefined, params = {}) {
        // Id can be a list of ids delimited by a comma
        await this.loadMarkets ();
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchOrder () requires a symbol argument');
        }
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
            'order_id': id,
        };
        const response = await this.privatePostOrdersInfo (this.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
        //      }
        //
        const result = this.safeValue (response, 'data', []);
        const numOrders = result.length;
        if (numOrders === 1) {
            return this.parseOrder (result[0]);
        } else {
            const parsedOrders = [];
            for (let i = 0; i < numOrders; i++) {
                const parsedOrder = this.parseOrder (result[i]);
                parsedOrders.push (parsedOrder);
            }
            return parsedOrders;
        }
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchMyTrades
         * @description fetch all trades made by the user
         * @param {str} symbol unified market symbol
         * @param {int|undefined} since the earliest time in ms to fetch trades for
         * @param {int|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html#trade-structure}
         */
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchMyTrades () requires a symbol argument');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        since = this.safeValue (params, 'start_date', since);
        params = this.omit (params, 'start_date');
        const request = {
            'symbol': market['id'],
            // 'start_date': String Start time yyyy-mm-dd, the maximum is today, the default is yesterday
            // 'end_date': String 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': String Initial transaction number inquiring
            // 'direct': String inquire direction,The default is the 'next' which is the positive sequence of dealing time,the 'prev' is inverted order of dealing time
            // 'size': String Query the number of defaults to 100
        };
        if (limit !== undefined) {
            request['size'] = limit;
        }
        if (since !== undefined) {
            request['start_date'] = this.ymd (since, '-'); // max query 2 days ago
        }
        const response = await this.privatePostTransactionHistory (this.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
        //      }
        //
        const trades = this.safeValue (response, 'data', []);
        return this.parseTrades (trades, market, since, limit);
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchOrders
         * @description fetches information on multiple orders made by the user
         * @param {str} symbol unified market symbol of the market orders were made in
         * @param {int|undefined} since the earliest time in ms to fetch orders for
         * @param {int|undefined} 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]{@link 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 === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchOrders() requires a symbol argument');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        if (limit === undefined) {
            limit = 100;
        }
        const 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
        };
        const response = await this.privatePostSupplementOrdersInfoHistory (this.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
        //      }
        //
        const result = this.safeValue (response, 'data', {});
        const orders = this.safeValue (result, 'orders', []);
        return this.parseOrders (orders, market, since, limit);
    }

    async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchOpenOrders
         * @description fetch all unfilled currently open orders
         * @param {str} symbol unified market symbol
         * @param {int|undefined} since the earliest time in ms to fetch open orders for
         * @param {int|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
         */
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        if (limit === undefined) {
            limit = 100;
        }
        const request = {
            'symbol': market['id'],
            'current_page': 1,
            'page_length': limit,
        };
        const response = await this.privatePostSupplementOrdersInfoNoDeal (this.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
        //     }
        //
        const result = this.safeValue (response, 'data', {});
        const orders = this.safeValue (result, 'orders', []);
        return this.parseOrders (orders, market, since, limit);
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#cancelOrder
         * @description 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]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
         */
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
        }
        await this.loadMarkets ();
        const clientOrderId = this.safeString2 (params, 'origClientOrderId', 'clientOrderId');
        params = this.omit (params, [ 'origClientOrderId', 'clientOrderId' ]);
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        if (clientOrderId !== undefined) {
            request['origClientOrderId'] = clientOrderId;
        }
        request['orderId'] = id;
        const response = await this.privatePostSupplementCancelOrder (this.extend (request, params));
        //
        //   {
        //      "result":true,
        //      "data":{
        //          "executedQty":0.0,
        //          "price":0.05,
        //          "origQty":100.0,
        //          "tradeType":"buy",
        //          "status":0
        //          },
        //      "error_code":0,
        //      "ts":1648501286196
        //  }
        const result = this.safeValue (response, 'data', {});
        return result;
    }

    async cancelAllOrders (symbol = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#cancelAllOrders
         * @description 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]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
         */
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' cancelAllOrders() requires a symbol argument');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'symbol': market['id'],
        };
        const response = await this.privatePostSupplementCancelOrderBySymbol (this.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
        //      }
        //
        const result = this.safeValue (response, 'data', []);
        return result;
    }

    async fetchDepositAddress (code, params = {}) {
        /**
         * @method
         * @name lbank2#fetchDepositAddress
         * @description fetch the deposit address for a currency associated with this account
         * @param {str} code unified currency code
         * @param {dict} params extra parameters specific to the lbank2 api endpoint
         * @returns {dict} an [address structure]{@link https://docs.ccxt.com/en/latest/manual.html#address-structure}
         */
        await this.loadMarkets ();
        let method = this.safeString (params, 'method');
        params = this.omit (params, 'method');
        if (method === undefined) {
            const options = this.safeValue (this.options, 'fetchDepositAddress', {});
            method = this.safeString (options, 'method', 'fetchPrivateTradingFees');
        }
        return await this[method] (code, params);
    }

    async fetchDepositAddressDefault (code, params = {}) {
        await this.loadMarkets ();
        const currency = this.currency (code);
        const request = {
            'assetCode': currency['id'],
        };
        const networks = this.safeValue (this.options, 'networks');
        let network = this.safeStringUpper (params, 'network');
        network = this.safeString (networks, network, network);
        if (network !== undefined) {
            request['netWork'] = network; // ... yes, really lol
            params = this.omit (params, 'network');
        }
        const response = await this.privatePostGetDepositAddress (this.extend (request, params));
        //
        //      {
        //          "result":true,
        //          "data":{
        //              "assetCode":"usdt",
        //              "address":"0xc85689d37ca650bf2f2161364cdedee21eb6ca53",
        //              "memo":null,
        //              "netWork":"bep20(bsc)"
        //              },
        //          "error_code":0,
        //          "ts":1648075865103
        //      }
        //
        const result = this.safeValue (response, 'data');
        const address = this.safeString (result, 'address');
        const tag = this.safeString (result, 'memo');
        const networkId = this.safeString (result, 'netWork');
        const inverseNetworks = this.safeValue (this.options, 'inverse-networks', {});
        const networkCode = this.safeStringUpper (inverseNetworks, networkId, networkId);
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': networkCode,
            'info': response,
        };
    }

    async fetchDepositAddressSupplement (code, params = {}) {
        // returns the address for whatever the default network is...
        await this.loadMarkets ();
        const currency = this.currency (code);
        const request = {
            'coin': currency['id'],
        };
        const networks = this.safeValue (this.options, 'networks');
        let network = this.safeStringUpper (params, 'network');
        network = this.safeString (networks, network, network);
        if (network !== undefined) {
            request['networkName'] = network;
            params = this.omit (params, 'network');
        }
        const response = await this.privatePostSupplementGetDepositAddress (this.extend (request, params));
        //
        //      {
        //          "result":true,
        //          "data":{
        //              "address":"TDxtabCC8iQwaxUUrPcE4WL2jArGAfvQ5A",
        //              "memo":null,
        //              "coin":"usdt"
        //              },
        //          "error_code":0,
        //          "ts":1648073818880
        //     }
        //
        const result = this.safeValue (response, 'data');
        const address = this.safeString (result, 'address');
        const tag = this.safeString (result, 'memo');
        const inverseNetworks = this.safeValue (this.options, 'inverse-networks', {});
        const networkCode = this.safeStringUpper (inverseNetworks, network, network);
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': networkCode, // will be undefined if not specified in request
            'info': response,
        };
    }

    async withdraw (code, amount, address, tag = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#withdraw
         * @description 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|undefined} tag
         * @param {dict} params extra parameters specific to the lbank2 api endpoint
         * @returns {dict} a [transaction structure]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
         */
        [ tag, params ] = this.handleWithdrawTagAndParams (tag, params);
        this.checkAddress (address);
        await this.loadMarkets ();
        const fee = this.safeString (params, 'fee');
        params = this.omit (params, 'fee');
        if (fee === undefined) {
            throw new ArgumentsRequired (this.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, this can also be found in fetchTransactionFees ()');
        }
        const currency = this.currency (code);
        const 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 this 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 !== undefined) {
            request['memo'] = tag;
        }
        const network = this.safeStringUpper2 (params, 'network', 'networkName');
        params = this.omit (params, [ 'network', 'networkName' ]);
        const networks = this.safeValue (this.options, 'networks');
        const networkId = this.safeString (networks, network, network);
        if (networkId !== undefined) {
            request['networkName'] = networkId;
        }
        const response = await this.privatePostSupplementWithdraw (this.extend (request, params));
        //
        //      {
        //          "result":true,
        //          "data": {
        //              "fee":10.00000000000000000000,
        //              "withdrawId":1900376
        //              },
        //          "error_code":0,
        //          "ts":1648992501414
        //      }
        //
        const result = this.safeValue (response, 'data', {});
        return {
            'info': result,
            'id': this.safeString (result, 'withdrawId'),
        };
    }

    parseTransactionStatus (status, type) {
        const statuses = {
            'deposit': {
                '1': 'pending',
                '2': 'ok',
                '3': 'failed',
                '4': 'canceled',
                '5': 'transfer',
            },
            'withdrawal': {
                '1': 'pending',
                '2': 'canceled',
                '3': 'failed',
                '4': 'ok',
            },
        };
        return this.safeString (this.safeValue (statuses, type, {}), status, status);
    }

    parseTransaction (transaction, currency = undefined) {
        //
        // 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"
        //      }
        //
        const id = this.safeString (transaction, 'id');
        let type = undefined;
        if (id === undefined) {
            type = 'deposit';
        } else {
            type = 'withdrawal';
        }
        const txid = this.safeString (transaction, 'txId');
        const timestamp = this.safeInteger2 (transaction, 'insertTime', 'applyTime');
        const networks = this.safeValue (this.options, 'inverse-networks', {});
        const networkId = this.safeString (transaction, 'networkName');
        const network = this.safeString (networks, networkId, networkId);
        const address = this.safeString (transaction, 'address');
        let addressFrom = undefined;
        let addressTo = undefined;
        if (type === 'deposit') {
            addressFrom = address;
        } else {
            addressTo = address;
        }
        const amount = this.safeNumber (transaction, 'amount');
        const currencyId = this.safeString2 (transaction, 'coin', 'coid');
        const code = this.safeCurrencyCode (currencyId, currency);
        const status = this.parseTransactionStatus (this.safeString (transaction, 'status'), type);
        let fee = undefined;
        const feeCost = this.safeNumber (transaction, 'fee');
        if (feeCost !== undefined) {
            fee = {
                'cost': feeCost,
                'currency': code,
            };
        }
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'network': network,
            'address': address,
            'addressTo': addressTo,
            'addressFrom': addressFrom,
            'tag': undefined,
            'tagTo': undefined,
            'tagFrom': undefined,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': undefined,
            'comment': undefined,
            'internal': (status === 'transfer'),
            'fee': fee,
        };
    }

    async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchDeposits
         * @description fetch all deposits made to an account
         * @param {str|undefined} code unified currency code
         * @param {int|undefined} since the earliest time in ms to fetch deposits for
         * @param {int|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
         */
        await this.loadMarkets ();
        const 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
        };
        if (code !== undefined) {
            const currency = this.currency (code);
            request['coin'] = currency['id'];
        }
        if (since !== undefined) {
            request['startTime'] = since;
        }
        const response = await this.privatePostSupplementDepositHistory (this.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
        //      }
        //
        const data = this.safeValue (response, 'data', {});
        const deposits = this.safeValue (data, 'depositOrders', []);
        return this.parseTransactions (deposits, code, since, limit);
    }

    async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchWithdrawals
         * @description fetch all withdrawals made from an account
         * @param {str|undefined} code unified currency code
         * @param {int|undefined} since the earliest time in ms to fetch withdrawals for
         * @param {int|undefined} 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]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
         */
        await this.loadMarkets ();
        const 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
        };
        if (code !== undefined) {
            const currency = this.currency (code);
            request['coin'] = currency['id'];
        }
        if (since !== undefined) {
            request['startTime'] = since;
        }
        const response = await this.privatePostSupplementWithdraws (this.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
        //      }
        //
        const data = this.safeValue (response, 'data', {});
        const withdraws = this.safeValue (data, 'withdraws', []);
        return this.parseTransactions (withdraws, code, since, limit);
    }

    async fetchTransactionFees (codes = undefined, params = {}) {
        /**
         * @method
         * @name lbank2#fetchTransactionFees
         * @description fetch transaction fees
         * @param {[str]|undefined} codes not used by lbank2 fetchTransactionFees ()
         * @param {dict} params extra parameters specific to the lbank2 api endpoint
         * @returns {dict} a list of [fee structures]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure}
         */
        // private only returns information for currencies with non-zero balance
        await this.loadMarkets ();
        const isAuthorized = this.checkRequiredCredentials (false);
        let result = undefined;
        if (isAuthorized === true) {
            let method = this.safeString (params, 'method');
            params = this.omit (params, 'method');
            if (method === undefined) {
                const options = this.safeValue (this.options, 'fetchTransactionFees', {});
                method = this.safeString (options, 'method', 'fetchPrivateFundingFees');
            }
            result = await this[method] (params);
        } else {
            result = await this.fetchPublicFundingFees (params);
        }
        return result;
    }

    async fetchPrivateFundingFees (params = {}) {
        // complete response
        // incl. for coins which undefined in public method
        await this.loadMarkets ();
        const response = await this.privatePostSupplementUserInfo ();
        const result = this.safeValue (response, 'data', []);
        const withdrawFees = {};
        for (let i = 0; i < result.length; i++) {
            const entry = result[i];
            const currencyId = this.safeString (entry, 'coin');
            const code = this.safeCurrencyCode (currencyId);
            const networkList = this.safeValue (entry, 'networkList', []);
            withdrawFees[code] = {};
            for (let j = 0; j < networkList.length; j++) {
                const networkEntry = networkList[j];
                const networkId = this.safeString (networkEntry, 'name');
                const networkCode = this.safeString (this.options['inverse-networks'], networkId, networkId);
                const fee = this.safeNumber (networkEntry, 'withdrawFee');
                if (fee !== undefined) {
                    withdrawFees[code][networkCode] = fee;
                }
            }
        }
        return {
            'withdraw': withdrawFees,
            'deposit': {},
            'info': response,
        };
    }

    async fetchPublicFundingFees (params = {}) {
        // extremely incomplete response
        // vast majority fees undefined
        await this.loadMarkets ();
        const code = this.safeString2 (params, 'coin', 'assetCode');
        params = this.omit (params, [ 'coin', 'assetCode' ]);
        const request = {};
        if (code !== undefined) {
            const currency = this.currency (code);
            request['assetCode'] = currency['id'];
        }
        const response = await this.publicGetWithdrawConfigs (this.extend (request, params));
        const result = this.safeValue (response, 'data', []);
        const withdrawFees = {};
        for (let i = 0; i < result.length; i++) {
            const item = result[i];
            const canWithdraw = this.safeString (item, 'canWithDraw');
            if (canWithdraw === 'true') {
                const currencyId = this.safeString (item, 'assetCode');
                const code = this.safeCurrencyCode (currencyId);
                const chain = this.safeString (item, 'chain');
                let network = this.safeString (this.options['inverse-networks'], chain, chain);
                if (network === undefined) {
                    network = code;
                }
                const fee = this.safeString (item, 'fee');
                if (withdrawFees[code] === undefined) {
                    withdrawFees[code] = {};
                }
                withdrawFees[code][network] = fee;
            }
        }
        return {
            'withdraw': withdrawFees,
            'deposit': {},
            'info': response,
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let query = this.omit (params, this.extractParams (path));
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        // Every endpoint ends with ".do"
        url += '.do';
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (this.keysort (query));
            }
        } else {
            this.checkRequiredCredentials ();
            const timestamp = this.milliseconds ().toString ();
            const echostr = this.uuid22 () + this.uuid16 ();
            query = this.extend ({
                'api_key': this.apiKey,
            }, query);
            let signatureMethod = undefined;
            if (this.secret.length > 32) {
                signatureMethod = 'RSA';
            } else {
                signatureMethod = 'HmacSHA256';
            }
            const auth = this.rawencode (this.keysort (this.extend ({
                'echostr': echostr,
                'signature_method': signatureMethod,
                'timestamp': timestamp,
            }, query)));
            const encoded = this.encode (auth);
            const hash = this.hash (encoded);
            const uppercaseHash = hash.toUpperCase ();
            let sign = undefined;
            if (signatureMethod === 'RSA') {
                const cacheSecretAsPem = this.safeValue (this.options, 'cacheSecretAsPem', true);
                let pem = undefined;
                if (cacheSecretAsPem) {
                    pem = this.safeValue (this.options, 'pem');
                    if (pem === undefined) {
                        pem = this.convertSecretToPem (this.encode (this.secret));
                        this.options['pem'] = pem;
                    }
                } else {
                    pem = this.convertSecretToPem (this.encode (this.secret));
                }
                const encodedPem = this.encode (pem);
                sign = this.binaryToBase64 (this.rsa (uppercaseHash, encodedPem, 'RS256'));
            } else if (signatureMethod === 'HmacSHA256') {
                sign = this.hmac (this.encode (uppercaseHash), this.encode (this.secret));
            }
            query['sign'] = sign;
            body = this.urlencode (this.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 };
    }

    convertSecretToPem (secret) {
        const lineLength = 64;
        const secretLength = secret.length - 0;
        let numLines = parseInt (secretLength / lineLength);
        numLines = this.sum (numLines, 1);
        let pem = "-----BEGIN PRIVATE KEY-----\n"; // eslint-disable-line
        for (let i = 0; i < numLines; i++) {
            const start = i * lineLength;
            const end = this.sum (start, lineLength);
            pem += this.secret.slice (start, end) + "\n"; // eslint-disable-line
        }
        return pem + '-----END PRIVATE KEY-----';
    }

    handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
        if (response === undefined) {
            return;
        }
        const success = this.safeString (response, 'result');
        if (success === 'false') {
            const errorCode = this.safeString (response, 'error_code');
            const message = this.safeString ({
                '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 this 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, this.json (response));
            const ErrorClass = this.safeValue ({
                '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 this 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);
            throw new ErrorClass (message);
        }
    }
};