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

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

from ccxt_zagrava.async_support.base.exchange import Exchange
from ccxt_zagrava.abstract.poloniex import ImplicitAPI
import hashlib
from ccxt_zagrava.base.types import Balances, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, TransferEntry
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import AuthenticationError
from ccxt_zagrava.base.errors import PermissionDenied
from ccxt_zagrava.base.errors import AccountSuspended
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import OrderNotFound
from ccxt_zagrava.base.errors import NotSupported
from ccxt_zagrava.base.errors import ExchangeNotAvailable
from ccxt_zagrava.base.errors import OnMaintenance
from ccxt_zagrava.base.errors import RequestTimeout
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class poloniex(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(poloniex, self).describe(), {
            'id': 'poloniex',
            'name': 'Poloniex',
            'countries': ['US'],
            # 200 requests per second for some unauthenticated market endpoints => 1000ms / 200 = 5ms between requests
            'rateLimit': 5,
            'certified': False,
            'pro': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': None,  # has but not fully implemented
                'swap': False,
                'future': False,
                'option': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createDepositAddress': True,
                'createMarketBuyOrderWithCost': True,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchClosedOrder': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDeposits': True,
                'fetchDepositsWithdrawals': True,
                'fetchDepositWithdrawFee': 'emulated',
                'fetchDepositWithdrawFees': True,
                'fetchFundingRate': False,
                'fetchMarginMode': False,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrder': False,
                'fetchOpenOrders': True,  # True endpoint for open orders
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrderTrades': True,  # True endpoint for trades of a single open or closed order
                'fetchPosition': False,
                'fetchPositionMode': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': True,
                'fetchTransactions': 'emulated',
                'fetchTransfer': False,
                'fetchTransfers': False,
                'fetchWithdrawals': True,
                'sandbox': True,
                'transfer': True,
                'withdraw': True,
            },
            'timeframes': {
                '1m': 'MINUTE_1',
                '5m': 'MINUTE_5',
                '10m': 'MINUTE_10',
                '15m': 'MINUTE_15',
                '30m': 'MINUTE_30',
                '1h': 'HOUR_1',
                '2h': 'HOUR_2',
                '4h': 'HOUR_4',
                '6h': 'HOUR_6',
                '12h': 'HOUR_12',
                '1d': 'DAY_1',
                '3d': 'DAY_3',
                '1w': 'WEEK_1',
                '1M': 'MONTH_1',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg',
                'api': {
                    'rest': 'https://api.poloniex.com',
                },
                'test': {
                    'rest': 'https://sand-spot-api-gateway.poloniex.com',
                },
                'www': 'https://www.poloniex.com',
                'doc': 'https://docs.poloniex.com',
                'fees': 'https://poloniex.com/fees',
                'referral': 'https://poloniex.com/signup?c=UBFZJRPJ',
            },
            'api': {
                'public': {
                    'get': {
                        'markets': 20,
                        'markets/{symbol}': 1,
                        'currencies': 20,
                        'currencies/{currency}': 20,
                        'v2/currencies': 20,
                        'v2/currencies/{currency}': 20,
                        'timestamp': 1,
                        'markets/price': 1,
                        'markets/{symbol}/price': 1,
                        'markets/markPrice': 1,
                        'markets/{symbol}/markPrice': 1,
                        'markets/{symbol}/markPriceComponents': 1,
                        'markets/{symbol}/orderBook': 1,
                        'markets/{symbol}/candles': 1,
                        'markets/{symbol}/trades': 20,
                        'markets/ticker24h': 20,
                        'markets/{symbol}/ticker24h': 20,
                        'markets/collateralInfo': 1,
                        'markets/{currency}/collateralInfo': 1,
                        'markets/borrowRatesInfo': 1,
                    },
                },
                'private': {
                    'get': {
                        'accounts': 4,
                        'accounts/balances': 4,
                        'accounts/{id}/balances': 4,
                        'accounts/activity': 20,
                        'accounts/transfer': 20,
                        'accounts/transfer/{id}': 4,
                        'feeinfo': 20,
                        'accounts/interest/history': 1,
                        'subaccounts': 4,
                        'subaccounts/balances': 20,
                        'subaccounts/{id}/balances': 4,
                        'subaccounts/transfer': 20,
                        'subaccounts/transfer/{id}': 4,
                        'wallets/addresses': 20,
                        'wallets/addresses/{currency}': 20,
                        'wallets/activity': 20,
                        'margin/accountMargin': 4,
                        'margin/borrowStatus': 4,
                        'margin/maxSize': 4,
                        'orders': 20,
                        'orders/{id}': 4,
                        'orders/killSwitchStatus': 4,
                        'smartorders': 20,
                        'smartorders/{id}': 4,
                        'orders/history': 20,
                        'smartorders/history': 20,
                        'trades': 20,
                        'orders/{id}/trades': 4,
                    },
                    'post': {
                        'accounts/transfer': 4,
                        'subaccounts/transfer': 20,
                        'wallets/address': 20,
                        'wallets/withdraw': 20,
                        'v2/wallets/withdraw': 20,
                        'orders': 4,
                        'orders/batch': 20,
                        'orders/killSwitch': 4,
                        'smartorders': 4,
                    },
                    'delete': {
                        'orders/{id}': 4,
                        'orders/cancelByIds': 20,
                        'orders': 20,
                        'smartorders/{id}': 4,
                        'smartorders/cancelByIds': 20,
                        'smartorders': 20,
                    },
                    'put': {
                        'orders/{id}': 20,
                        'smartorders/{id}': 20,
                    },
                },
            },
            'fees': {
                'trading': {
                    'feeSide': 'get',
                    # starting from Jan 8 2020
                    'maker': self.parse_number('0.0009'),
                    'taker': self.parse_number('0.0009'),
                },
                'funding': {},
            },
            'commonCurrencies': {
                'AIR': 'AirCoin',
                'APH': 'AphroditeCoin',
                'BCC': 'BTCtalkcoin',
                'BCHABC': 'BCHABC',
                'BDG': 'Badgercoin',
                'BTM': 'Bitmark',
                'CON': 'Coino',
                'ETHTRON': 'ETH',
                'GOLD': 'GoldEagles',
                'GPUC': 'GPU',
                'HOT': 'Hotcoin',
                'ITC': 'Information Coin',
                'KEY': 'KEYCoin',
                'MASK': 'NFTX Hashmasks Index',  # conflict with Mask Network
                'MEME': 'Degenerator Meme',  # Degenerator Meme migrated to Meme Inu, self exchange still has the old price
                'PLX': 'ParallaxCoin',
                'REPV2': 'REP',
                'STR': 'XLM',
                'SOC': 'SOCC',
                'TRADE': 'Unitrade',
                'TRXETH': 'TRX',
                'XAP': 'API Coin',
                # self is not documented in the API docs for Poloniex
                # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/7084
                # when the user calls withdraw('USDT', amount, address, tag, params)
                # with params = {'currencyToWithdrawAs': 'USDTTRON'}
                # or params = {'currencyToWithdrawAs': 'USDTETH'}
                # fetchWithdrawals('USDT') returns the corresponding withdrawals
                # with a USDTTRON or a USDTETH currency id, respectfully
                # therefore we have map them back to the original code USDT
                # otherwise the returned withdrawals are filtered out
                'USDTBSC': 'USDT',
                'USDTTRON': 'USDT',
                'USDTETH': 'USDT',
                'UST': 'USTC',
            },
            'options': {
                'createMarketBuyOrderRequiresPrice': True,
                'networks': {
                    'BEP20': 'BSC',
                    'ERC20': 'ETH',
                    'TRC20': 'TRON',
                },
                'limits': {
                    'cost': {
                        'min': {
                            'BTC': 0.0001,
                            'ETH': 0.0001,
                            'USDT': 1.0,
                            'TRX': 100,
                            'BNB': 0.06,
                            'USDC': 1.0,
                            'USDJ': 1.0,
                            'TUSD': 0.0001,
                            'DAI': 1.0,
                            'PAX': 1.0,
                            'BUSD': 1.0,
                        },
                    },
                },
                'accountsByType': {
                    'spot': 'spot',
                    'future': 'futures',
                },
                'accountsById': {
                    'exchange': 'spot',
                    'futures': 'future',
                },
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'exact': {
                    # General
                    '500': ExchangeNotAvailable,  # Internal System Error
                    '603': RequestTimeout,  # Internal Request Timeout
                    '601': BadRequest,  # Invalid Parameter
                    '415': ExchangeError,  # System Error
                    '602': ArgumentsRequired,  # Missing Required Parameters
                    # Accounts
                    '21604': BadRequest,  # Invalid UserId
                    '21600': AuthenticationError,  # Account Not Found
                    '21605': AuthenticationError,  # Invalid Account Type
                    '21102': ExchangeError,  # Invalid Currency
                    '21100': AuthenticationError,  # Invalid account
                    '21704': AuthenticationError,  # Missing UserId and/or AccountId
                    '21700': BadRequest,  # Error updating accounts
                    '21705': BadRequest,  # Invalid currency type
                    '21707': ExchangeError,  # Internal accounts Error
                    '21708': BadRequest,  # Currency not available to User
                    '21601': AccountSuspended,  # Account locked. Contact support
                    '21711': ExchangeError,  # Currency locked. Contact support
                    '21709': InsufficientFunds,  # Insufficient balance
                    '250000': ExchangeError,  # Transfer error. Try again later
                    '250001': BadRequest,  # Invalid toAccount for transfer
                    '250002': BadRequest,  # Invalid fromAccount for transfer
                    '250003': BadRequest,  # Invalid transfer amount
                    '250004': BadRequest,  # Transfer is not supported
                    '250005': InsufficientFunds,  # Insufficient transfer balance
                    '250008': BadRequest,  # Invalid transfer currency
                    '250012': ExchangeError,  # Futures account is not valid
                    # Trading
                    '21110': BadRequest,  # Invalid quote currency
                    '10040': BadSymbol,  # Invalid symbol
                    '10060': ExchangeError,  # Symbol setup error
                    '10020': BadSymbol,  # Invalid currency
                    '10041': BadSymbol,  # Symbol frozen for trading
                    '21340': OnMaintenance,  # No order creation/cancelation is allowed is in Maintenane Mode
                    '21341': InvalidOrder,  # Post-only orders type allowed is in Post Only Mode
                    '21342': InvalidOrder,  # Price is higher than highest bid is in Maintenance Mode
                    '21343': InvalidOrder,  # Price is lower than lowest bid is in Maintenance Mode
                    '21351': AccountSuspended,  # Trading for self account is frozen. Contact support
                    '21352': BadSymbol,  # Trading for self currency is frozen
                    '21353': PermissionDenied,  # Trading for US customers is not supported
                    '21354': PermissionDenied,  # Account needs to be verified via email before trading is enabled. Contact support
                    '21359': OrderNotFound,  # {"code" : 21359, "message" : "Order was already canceled or filled."}
                    '21360': InvalidOrder,  # {"code" : 21360, "message" : "Order size exceeds the limit.Please enter a smaller amount and try again."}
                    '24106': BadRequest,  # Invalid market depth
                    '24201': ExchangeNotAvailable,  # Service busy. Try again later
                    # Orders
                    '21301': OrderNotFound,  # Order not found
                    '21302': ExchangeError,  # Batch cancel order error
                    '21304': ExchangeError,  # Order is filled
                    '21305': OrderNotFound,  # Order is canceled
                    '21307': ExchangeError,  # Error during Order Cancelation
                    '21309': InvalidOrder,  # Order price must be greater than 0
                    '21310': InvalidOrder,  # Order price must be less than max price
                    '21311': InvalidOrder,  # Order price must be greater than min price
                    '21312': InvalidOrder,  # Client orderId already exists
                    '21314': InvalidOrder,  # Max limit of open orders(2000) exceeded
                    '21315': InvalidOrder,  # Client orderId exceeded max length of 17 digits
                    '21317': InvalidOrder,  # Amount must be greater than 0
                    '21319': InvalidOrder,  # Invalid order side
                    '21320': InvalidOrder,  # Invalid order type
                    '21321': InvalidOrder,  # Invalid timeInForce value
                    '21322': InvalidOrder,  # Amount is less than minAmount trade limit
                    '21324': BadRequest,  # Invalid account type
                    '21327': InvalidOrder,  # Order pice must be greater than 0
                    '21328': InvalidOrder,  # Order quantity must be greater than 0
                    '21330': InvalidOrder,  # Quantity is less than minQuantity trade limit
                    '21335': InvalidOrder,  # Invalid priceScale for self symbol
                    '21336': InvalidOrder,  # Invalid quantityScale for self symbol
                    '21337': InvalidOrder,  # Invalid amountScale for self symbol
                    '21344': InvalidOrder,  # Value of limit param is greater than max value of 100
                    '21345': InvalidOrder,  # Value of limit param value must be greater than 0
                    '21346': InvalidOrder,  # Order Id must be of type Long
                    '21348': InvalidOrder,  # Order type must be LIMIT_MAKER
                    '21347': InvalidOrder,  # Stop price must be greater than 0
                    '21349': InvalidOrder,  # Order value is too large
                    '21350': InvalidOrder,  # Amount must be greater than 1 USDT
                    '21355': ExchangeError,  # Interval between startTime and endTime in trade/order history has exceeded 7 day limit
                    '21356': BadRequest,  # Order size would cause too much price movement. Reduce order size.
                    '24101': BadSymbol,  # Invalid symbol
                    '24102': InvalidOrder,  # Invalid K-line type
                    '24103': InvalidOrder,  # Invalid endTime
                    '24104': InvalidOrder,  # Invalid amount
                    '24105': InvalidOrder,  # Invalid startTime
                    '25020': InvalidOrder,  # No active kill switch
                    # Smartorders
                    '25000': InvalidOrder,  # Invalid userId
                    '25001': InvalidOrder,  # Invalid parameter
                    '25002': InvalidOrder,  # Invalid userId.
                    '25003': ExchangeError,  # Unable to place order
                    '25004': InvalidOrder,  # Client orderId already exists
                    '25005': ExchangeError,  # Unable to place smart order
                    '25006': InvalidOrder,  # OrderId and clientOrderId already exists
                    '25007': InvalidOrder,  # Invalid orderid
                    '25008': InvalidOrder,  # Both orderId and clientOrderId are required
                    '25009': ExchangeError,  # Failed to cancel order
                    '25010': PermissionDenied,  # Unauthorized to cancel order
                    '25011': InvalidOrder,  # Failed to cancel due to invalid paramters
                    '25012': ExchangeError,  # Failed to cancel
                    '25013': OrderNotFound,  # Failed to cancel were not found
                    '25014': OrderNotFound,  # Failed to cancel were not found
                    '25015': OrderNotFound,  # Failed to cancel orders exist
                    '25016': ExchangeError,  # Failed to cancel to release funds
                    '25017': ExchangeError,  # No orders were canceled
                    '25018': BadRequest,  # Invalid accountType
                    '25019': BadSymbol,  # Invalid symbol
                },
                'broad': {
                },
            },
        })

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     [
        #         [
        #             "22814.01",
        #             "22937.42",
        #             "22832.57",
        #             "22937.42",
        #             "3916.58764051",
        #             "0.171199",
        #             "2982.64647063",
        #             "0.130295",
        #             33,
        #             0,
        #             "22877.449915304470460711",
        #             "MINUTE_5",
        #             1659664800000,
        #             1659665099999
        #         ]
        #     ]
        #
        return [
            self.safe_integer(ohlcv, 12),
            self.safe_number(ohlcv, 2),
            self.safe_number(ohlcv, 1),
            self.safe_number(ohlcv, 0),
            self.safe_number(ohlcv, 3),
            self.safe_number(ohlcv, 5),
        ]

    async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :see: https://docs.poloniex.com/#public-endpoints-market-data-candles
        :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 [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', False)
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 500)
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'interval': self.safe_string(self.timeframes, timeframe, timeframe),
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            # limit should in between 100 and 500
            request['limit'] = limit
        request, params = self.handle_until_option('endTime', request, params)
        response = await self.publicGetMarketsSymbolCandles(self.extend(request, params))
        #
        #     [
        #         [
        #             "22814.01",
        #             "22937.42",
        #             "22832.57",
        #             "22937.42",
        #             "3916.58764051",
        #             "0.171199",
        #             "2982.64647063",
        #             "0.130295",
        #             33,
        #             0,
        #             "22877.449915304470460711",
        #             "MINUTE_5",
        #             1659664800000,
        #             1659665099999
        #         ]
        #     ]
        #
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    async def load_markets(self, reload=False, params={}):
        markets = await super(poloniex, self).load_markets(reload, params)
        currenciesByNumericId = self.safe_value(self.options, 'currenciesByNumericId')
        if (currenciesByNumericId is None) or reload:
            self.options['currenciesByNumericId'] = self.index_by(self.currencies, 'numericId')
        return markets

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for poloniex
        :see: https://docs.poloniex.com/#public-endpoints-reference-data-symbol-information
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        markets = await self.publicGetMarkets(params)
        #
        #     [
        #         {
        #             "symbol" : "BTS_BTC",
        #             "baseCurrencyName" : "BTS",
        #             "quoteCurrencyName" : "BTC",
        #             "displayName" : "BTS/BTC",
        #             "state" : "NORMAL",
        #             "visibleStartTime" : 1659018816626,
        #             "tradableStartTime" : 1659018816626,
        #             "symbolTradeLimit" : {
        #                 "symbol" : "BTS_BTC",
        #                 "priceScale" : 10,
        #                 "quantityScale" : 0,
        #                 "amountScale" : 8,
        #                 "minQuantity" : "100",
        #                 "minAmount" : "0.00001",
        #                 "highestBid" : "0",
        #                 "lowestAsk" : "0"
        #             }
        #         }
        #     ]
        #
        return self.parse_markets(markets)

    def parse_market(self, market: dict) -> Market:
        id = self.safe_string(market, 'symbol')
        baseId = self.safe_string(market, 'baseCurrencyName')
        quoteId = self.safe_string(market, 'quoteCurrencyName')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        state = self.safe_string(market, 'state')
        active = state == 'NORMAL'
        symbolTradeLimit = self.safe_value(market, 'symbolTradeLimit')
        # these are known defaults
        return {
            'id': id,
            'symbol': base + '/' + quote,
            'base': base,
            'quote': quote,
            'settle': None,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': None,
            'type': 'spot',
            'spot': True,
            'margin': False,
            'swap': False,
            'future': False,
            'option': False,
            'active': active,
            'contract': False,
            'linear': None,
            'inverse': None,
            'contractSize': None,
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': self.parse_number(self.parse_precision(self.safe_string(symbolTradeLimit, 'quantityScale'))),
                'price': self.parse_number(self.parse_precision(self.safe_string(symbolTradeLimit, 'priceScale'))),
            },
            'limits': {
                'amount': {
                    'min': self.safe_number(symbolTradeLimit, 'minQuantity'),
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': self.safe_number(symbolTradeLimit, 'minAmount'),
                    'max': None,
                },
            },
            'created': self.safe_integer(market, 'tradableStartTime'),
            'info': market,
        }

    async def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        :see: https://docs.poloniex.com/#public-endpoints-reference-data-system-timestamp
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = await self.publicGetTimestamp(params)
        return self.safe_integer(response, 'serverTime')

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        #     {
        #         "symbol" : "BTC_USDT",
        #         "open" : "26053.33",
        #         "low" : "26053.33",
        #         "high" : "26798.02",
        #         "close" : "26447.58",
        #         "quantity" : "6116.210188",
        #         "amount" : "161082122.88450926",
        #         "tradeCount" : "134709",
        #         "startTime" : "1692784440000",
        #         "closeTime" : "1692870839630",
        #         "displayName" : "BTC/USDT",
        #         "dailyChange" : "0.0151",
        #         "bid" : "26447.57",
        #         "bidQuantity" : "0.016313",
        #         "ask" : "26447.58",
        #         "askQuantity" : "0.068307",
        #         "ts" : "1692870845446",
        #         "markPrice" : "26444.11"
        #     }
        #
        timestamp = self.safe_integer(ticker, 'ts')
        marketId = self.safe_string(ticker, 'symbol')
        market = self.safe_market(marketId)
        close = self.safe_string(ticker, 'close')
        relativeChange = self.safe_string(ticker, 'dailyChange')
        percentage = Precise.string_mul(relativeChange, '100')
        bidVolume = self.safe_string(ticker, 'bidQuantity')
        askVolume = self.safe_string(ticker, 'askQuantity')
        return self.safe_ticker({
            'id': marketId,
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': self.safe_string(ticker, 'bid'),
            'bidVolume': bidVolume,
            'ask': self.safe_string(ticker, 'ask'),
            'askVolume': askVolume,
            'vwap': None,
            'open': self.safe_string(ticker, 'open'),
            'close': close,
            'last': close,
            'previousClose': None,
            'change': None,
            'percentage': percentage,
            'average': None,
            'baseVolume': self.safe_string(ticker, 'quantity'),
            'quoteVolume': self.safe_string(ticker, 'amount'),
            'info': ticker,
        }, market)

    async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :see: https://docs.poloniex.com/#public-endpoints-market-data-ticker
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        response = await self.publicGetMarketsTicker24h(params)
        #
        #     [
        #         {
        #              "symbol" : "BTC_USDT",
        #              "open" : "26053.33",
        #              "low" : "26053.33",
        #              "high" : "26798.02",
        #              "close" : "26447.58",
        #              "quantity" : "6116.210188",
        #              "amount" : "161082122.88450926",
        #              "tradeCount" : "134709",
        #              "startTime" : "1692784440000",
        #              "closeTime" : "1692870839630",
        #              "displayName" : "BTC/USDT",
        #              "dailyChange" : "0.0151",
        #              "bid" : "26447.57",
        #              "bidQuantity" : "0.016313",
        #              "ask" : "26447.58",
        #              "askQuantity" : "0.068307",
        #              "ts" : "1692870845446",
        #              "markPrice" : "26444.11"
        #         }
        #     ]
        #
        return self.parse_tickers(response, symbols)

    async def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :see: https://docs.poloniex.com/#public-endpoints-reference-data-currency-information
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = await self.publicGetCurrencies(self.extend(params, {'includeMultiChainCurrencies': True}))
        #
        #     [
        #         {
        #             "1CR": {
        #                 "id": 1,
        #                 "name": "1CRedit",
        #                 "description": "BTC Clone",
        #                 "type": "address",
        #                 "withdrawalFee": "0.01000000",
        #                 "minConf": 10000,
        #                 "depositAddress": null,
        #                 "blockchain": "1CR",
        #                 "delisted": False,
        #                 "tradingState": "NORMAL",
        #                 "walletState": "DISABLED",
        #                 "walletDepositState": "DISABLED",
        #                 "walletWithdrawalState": "DISABLED",
        #                 "parentChain": null,
        #                 "isMultiChain": False,
        #                 "isChildChain": False,
        #                 "childChains": []
        #             }
        #         }
        #     ]
        #
        result: dict = {}
        for i in range(0, len(response)):
            item = self.safe_value(response, i)
            ids = list(item.keys())
            id = self.safe_value(ids, 0)
            currency = self.safe_value(item, id)
            code = self.safe_currency_code(id)
            name = self.safe_string(currency, 'name')
            networkId = self.safe_string(currency, 'blockchain')
            networkCode = None
            if networkId is not None:
                networkCode = self.network_id_to_code(networkId, code)
            delisted = self.safe_value(currency, 'delisted')
            walletEnabled = self.safe_string(currency, 'walletState') == 'ENABLED'
            depositEnabled = self.safe_string(currency, 'walletDepositState') == 'ENABLED'
            withdrawEnabled = self.safe_string(currency, 'walletWithdrawalState') == 'ENABLED'
            active = not delisted and walletEnabled and depositEnabled and withdrawEnabled
            numericId = self.safe_integer(currency, 'id')
            feeString = self.safe_string(currency, 'withdrawalFee')
            parentChain = self.safe_value(currency, 'parentChain')
            noParentChain = parentChain is None
            if self.safe_value(result, code) is None:
                result[code] = {
                    'id': id,
                    'code': code,
                    'info': None,
                    'name': name,
                    'active': active,
                    'deposit': depositEnabled,
                    'withdraw': withdrawEnabled,
                    'fee': self.parse_number(feeString),
                    'precision': None,
                    'limits': {
                        'amount': {
                            'min': None,
                            'max': None,
                        },
                        'deposit': {
                            'min': None,
                            'max': None,
                        },
                        'withdraw': {
                            'min': None,
                            'max': None,
                        },
                    },
                }
            minFeeString = self.safe_string(result[code], 'fee')
            if feeString is not None:
                minFeeString = feeString if (minFeeString is None) else Precise.string_min(feeString, minFeeString)
            depositAvailable = self.safe_value(result[code], 'deposit')
            depositAvailable = depositEnabled if (depositEnabled) else depositAvailable
            withdrawAvailable = self.safe_value(result[code], 'withdraw')
            withdrawAvailable = withdrawEnabled if (withdrawEnabled) else withdrawAvailable
            networks = self.safe_value(result[code], 'networks', {})
            if networkCode is not None:
                networks[networkCode] = {
                    'info': currency,
                    'id': networkId,
                    'network': networkCode,
                    'currencyId': id,
                    'numericId': numericId,
                    'deposit': depositEnabled,
                    'withdraw': withdrawEnabled,
                    'active': active,
                    'fee': self.parse_number(feeString),
                    'precision': None,
                    'limits': {
                        'amount': {
                            'min': None,
                            'max': None,
                        },
                        'withdraw': {
                            'min': None,
                            'max': None,
                        },
                        'deposit': {
                            'min': None,
                            'max': None,
                        },
                    },
                }
            result[code]['networks'] = networks
            info = self.safe_value(result[code], 'info', [])
            rawInfo: dict = {}
            rawInfo[id] = currency
            info.append(rawInfo)
            result[code]['info'] = info
            if noParentChain:
                result[code]['id'] = id
                result[code]['name'] = name
            result[code]['active'] = depositAvailable and withdrawAvailable
            result[code]['deposit'] = depositAvailable
            result[code]['withdraw'] = withdrawAvailable
            result[code]['fee'] = self.parse_number(minFeeString)
        return result

    async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :see: https://docs.poloniex.com/#public-endpoints-market-data-ticker
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = await self.publicGetMarketsSymbolTicker24h(self.extend(request, params))
        #
        #     {
        #         "symbol" : "BTC_USDT",
        #         "open" : "26053.33",
        #         "low" : "26053.33",
        #         "high" : "26798.02",
        #         "close" : "26447.58",
        #         "quantity" : "6116.210188",
        #         "amount" : "161082122.88450926",
        #         "tradeCount" : "134709",
        #         "startTime" : "1692784440000",
        #         "closeTime" : "1692870839630",
        #         "displayName" : "BTC/USDT",
        #         "dailyChange" : "0.0151",
        #         "bid" : "26447.57",
        #         "bidQuantity" : "0.016313",
        #         "ask" : "26447.58",
        #         "askQuantity" : "0.068307",
        #         "ts" : "1692870845446",
        #         "markPrice" : "26444.11"
        #     }
        #
        return self.parse_ticker(response, market)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades
        #
        #     {
        #         "id" : "60014521",
        #         "price" : "23162.94",
        #         "quantity" : "0.00009",
        #         "amount" : "2.0846646",
        #         "takerSide" : "SELL",
        #         "ts" : 1659684602042,
        #         "createTime" : 1659684602036
        #     }
        #
        # fetchMyTrades
        #
        #     {
        #         "id": "32164924331503616",
        #         "symbol": "LINK_USDT",
        #         "accountType": "SPOT",
        #         "orderId": "32164923987566592",
        #         "side": "SELL",
        #         "type": "MARKET",
        #         "matchRole": "TAKER",
        #         "createTime": 1648635115525,
        #         "price": "11",
        #         "quantity": "0.5",
        #         "amount": "5.5",
        #         "feeCurrency": "USDT",
        #         "feeAmount": "0.007975",
        #         "pageId": "32164924331503616",
        #         "clientOrderId": "myOwnId-321"
        #     }
        #
        # fetchOrderTrades(taker trades)
        #
        #     {
        #         "id": "30341456333942784",
        #         "symbol": "LINK_USDT",
        #         "accountType": "SPOT",
        #         "orderId": "30249408733945856",
        #         "side": "BUY",
        #         "type": "LIMIT",
        #         "matchRole": "MAKER",
        #         "createTime": 1648200366864,
        #         "price": "3.1",
        #         "quantity": "1",
        #         "amount": "3.1",
        #         "feeCurrency": "LINK",
        #         "feeAmount": "0.00145",
        #         "pageId": "30341456333942784",
        #         "clientOrderId": ""
        #     }
        #
        #
        id = self.safe_string_2(trade, 'id', 'tradeID')
        orderId = self.safe_string(trade, 'orderId')
        timestamp = self.safe_integer_2(trade, 'ts', 'createTime')
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market, '_')
        symbol = market['symbol']
        side = self.safe_string_lower_2(trade, 'side', 'takerSide')
        fee = None
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string(trade, 'quantity')
        costString = self.safe_string(trade, 'amount')
        feeCurrencyId = self.safe_string(trade, 'feeCurrency')
        feeCostString = self.safe_string(trade, 'feeAmount')
        if feeCostString is not None:
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            fee = {
                'cost': feeCostString,
                'currency': feeCurrencyCode,
            }
        return self.safe_trade({
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': self.safe_string_lower(trade, 'type'),
            'side': side,
            'takerOrMaker': self.safe_string_lower(trade, 'matchRole'),
            'price': priceString,
            'amount': amountString,
            'cost': costString,
            'fee': fee,
        }, market)

    async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get the list of most recent trades for a particular symbol
        :see: https://docs.poloniex.com/#public-endpoints-market-data-trades
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit
        trades = await self.publicGetMarketsSymbolTrades(self.extend(request, params))
        #
        #     [
        #         {
        #             "id" : "60014521",
        #             "price" : "23162.94",
        #             "quantity" : "0.00009",
        #             "amount" : "2.0846646",
        #             "takerSide" : "SELL",
        #             "ts" : 1659684602042,
        #             "createTime" : 1659684602036
        #         }
        #     ]
        #
        return self.parse_trades(trades, market, since, limit)

    async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all trades made by the user
        :see: https://docs.poloniex.com/#authenticated-endpoints-trades-trade-history
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch entries for
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchMyTrades', symbol, since, limit, params)
        market: Market = None
        if symbol is not None:
            market = self.market(symbol)
        request: dict = {
            # 'from': 12345678,  # A 'trade Id'. The query begins at ‘from'.
            # 'direction': 'PRE',  # PRE, NEXT The direction before or after ‘from'.
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        request, params = self.handle_until_option('endTime', request, params)
        response = await self.privateGetTrades(self.extend(request, params))
        #
        #     [
        #         {
        #             "id": "32164924331503616",
        #             "symbol": "LINK_USDT",
        #             "accountType": "SPOT",
        #             "orderId": "32164923987566592",
        #             "side": "SELL",
        #             "type": "MARKET",
        #             "matchRole": "TAKER",
        #             "createTime": 1648635115525,
        #             "price": "11",
        #             "quantity": "0.5",
        #             "amount": "5.5",
        #             "feeCurrency": "USDT",
        #             "feeAmount": "0.007975",
        #             "pageId": "32164924331503616",
        #             "clientOrderId": "myOwnId-321"
        #         }
        #     ]
        #
        result = self.parse_trades(response, market, since, limit)
        return result

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'NEW': 'open',
            'PARTIALLY_FILLED': 'open',
            'FILLED': 'closed',
            'PENDING_CANCEL': 'canceled',
            'PARTIALLY_CANCELED': 'canceled',
            'CANCELED': 'canceled',
            'FAILED': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # fetchOpenOrder
        #
        #     {
        #         "id" : "7xxxxxxxxxxxxxxx6",
        #         "clientOrderId" : "",
        #         "symbol" : "ETH_USDT",
        #         "state" : "NEW",
        #         "accountType" : "SPOT",
        #         "side" : "BUY",
        #         "type" : "LIMIT",
        #         "timeInForce" : "GTC",
        #         "quantity" : "0.001",
        #         "price" : "1600",
        #         "avgPrice" : "0",
        #         "amount" : "0",
        #         "filledQuantity" : "0",
        #         "filledAmount" : "0",
        #         "createTime" : 16xxxxxxxxx26,
        #         "updateTime" : 16xxxxxxxxx36
        #     }
        #
        # fetchOpenOrders
        #
        #     {
        #         "id": "24993088082542592",
        #         "clientOrderId": "",
        #         "symbol": "ELON_USDC",
        #         "state": "NEW",
        #         "accountType": "SPOT",
        #         "side": "SELL",
        #         "type": "MARKET",
        #         "timeInForce": "GTC",
        #         "quantity": "1.00",
        #         "price": "0.00",
        #         "avgPrice": "0.00",
        #         "amount": "0.00",
        #         "filledQuantity": "0.00",
        #         "filledAmount": "0.00",
        #         "createTime": 1646925216548,
        #         "updateTime": 1646925216548
        #     }
        #
        # createOrder, editOrder
        #
        #     {
        #         "id": "29772698821328896",
        #         "clientOrderId": "1234Abc"
        #     }
        #
        timestamp = self.safe_integer_2(order, 'timestamp', 'createTime')
        if timestamp is None:
            timestamp = self.parse8601(self.safe_string(order, 'date'))
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market, '_')
        symbol = market['symbol']
        resultingTrades = self.safe_value(order, 'resultingTrades')
        if not isinstance(resultingTrades, list):
            resultingTrades = self.safe_value(resultingTrades, self.safe_string(market, 'id', marketId))
        price = self.safe_string_2(order, 'price', 'rate')
        amount = self.safe_string(order, 'quantity')
        filled = self.safe_string(order, 'filledQuantity')
        status = self.parse_order_status(self.safe_string(order, 'state'))
        side = self.safe_string_lower(order, 'side')
        rawType = self.safe_string(order, 'type')
        type = self.parse_order_type(rawType)
        id = self.safe_string_n(order, ['orderNumber', 'id', 'orderId'])
        fee = None
        feeCurrency = self.safe_string(order, 'tokenFeeCurrency')
        feeCost: Str = None
        feeCurrencyCode: Str = None
        rate = self.safe_string(order, 'fee')
        if feeCurrency is None:
            feeCurrencyCode = market['base'] if (side == 'buy') else market['quote']
        else:
            # poloniex accepts a 30% discount to pay fees in TRX
            feeCurrencyCode = self.safe_currency_code(feeCurrency)
            feeCost = self.safe_string(order, 'tokenFee')
        if feeCost is not None:
            fee = {
                'rate': rate,
                'cost': feeCost,
                'currency': feeCurrencyCode,
            }
        clientOrderId = self.safe_string(order, 'clientOrderId')
        triggerPrice = self.safe_string_2(order, 'triggerPrice', 'stopPrice')
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': self.safe_integer(order, 'updateTime'),
            'status': status,
            'symbol': symbol,
            'type': type,
            'timeInForce': self.safe_string(order, 'timeInForce'),
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': triggerPrice,
            'triggerPrice': triggerPrice,
            'cost': None,
            'average': self.safe_string(order, 'avgPrice'),
            'amount': amount,
            'filled': filled,
            'remaining': None,
            'trades': resultingTrades,
            'fee': fee,
        }, market)

    def parse_order_type(self, status):
        statuses: dict = {
            'MARKET': 'market',
            'LIMIT': 'limit',
            'STOP-LIMIT': 'limit',
            'STOP-MARKET': 'market',
        }
        return self.safe_string(statuses, status, status)

    def parse_open_orders(self, orders, market, result):
        for i in range(0, len(orders)):
            order = orders[i]
            extended = self.extend(order, {
                'status': 'open',
                'type': 'limit',
                'side': order['type'],
                'price': order['rate'],
            })
            result.append(self.parse_order(extended, market))
        return result

    async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders
        :see: https://docs.poloniex.com/#authenticated-endpoints-orders-open-orders
        :see: https://docs.poloniex.com/#authenticated-endpoints-smart-orders-open-orders  # trigger orders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.stop]: set True to fetch trigger orders instead of regular orders
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market: Market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if limit is not None:
            request['limit'] = limit
        isTrigger = self.safe_value_2(params, 'trigger', 'stop')
        params = self.omit(params, ['trigger', 'stop'])
        response = None
        if isTrigger:
            response = await self.privateGetSmartorders(self.extend(request, params))
        else:
            response = await self.privateGetOrders(self.extend(request, params))
        #
        #     [
        #         {
        #             "id" : "7xxxxxxxxxxxxxxx6",
        #             "clientOrderId" : "",
        #             "symbol" : "ETH_USDT",
        #             "state" : "NEW",
        #             "accountType" : "SPOT",
        #             "side" : "BUY",
        #             "type" : "LIMIT",
        #             "timeInForce" : "GTC",
        #             "quantity" : "0.001",
        #             "price" : "1600",
        #             "avgPrice" : "0",
        #             "amount" : "0",
        #             "filledQuantity" : "0",
        #             "filledAmount" : "0",
        #             "stopPrice": "3750.00",              # for trigger orders
        #             "createTime" : 16xxxxxxxxx26,
        #             "updateTime" : 16xxxxxxxxx36
        #         }
        #     ]
        #
        extension: dict = {'status': 'open'}
        return self.parse_orders(response, market, since, limit, extension)

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://docs.poloniex.com/#authenticated-endpoints-orders-create-order
        :see: https://docs.poloniex.com/#authenticated-endpoints-smart-orders-create-order  # trigger orders
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param float [params.triggerPrice]: *spot only* The price at which a trigger order is triggered at
        :param float [params.cost]: *spot market buy only* the quote quantity that can be used alternative for the amount
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' createOrder() does not support ' + market['type'] + ' orders, only spot orders are accepted')
        request: dict = {
            'symbol': market['id'],
            'side': side,
            # 'timeInForce': timeInForce,
            # 'accountType': 'SPOT',
            # 'amount': amount,
        }
        triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
        request, params = self.order_request(symbol, type, side, amount, request, price, params)
        response = None
        if triggerPrice is not None:
            response = await self.privatePostSmartorders(self.extend(request, params))
        else:
            response = await self.privatePostOrders(self.extend(request, params))
        #
        #     {
        #         "id" : "78923648051920896",
        #         "clientOrderId" : ""
        #     }
        #
        response = self.extend(response, {
            'type': type,
            'side': side,
        })
        return self.parse_order(response, market)

    def order_request(self, symbol, type, side, amount, request, price=None, params={}):
        upperCaseType = type.upper()
        isMarket = upperCaseType == 'MARKET'
        isPostOnly = self.is_post_only(isMarket, upperCaseType == 'LIMIT_MAKER', params)
        triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
        params = self.omit(params, ['postOnly', 'triggerPrice', 'stopPrice'])
        if triggerPrice is not None:
            upperCaseType = 'STOP' if (price is None) else 'STOP_LIMIT'
            request['stopPrice'] = triggerPrice
        elif isPostOnly:
            upperCaseType = 'LIMIT_MAKER'
        request['type'] = upperCaseType
        if isMarket:
            if side == 'buy':
                quoteAmount = None
                createMarketBuyOrderRequiresPrice = True
                createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
                cost = self.safe_number(params, 'cost')
                params = self.omit(params, 'cost')
                if cost is not None:
                    quoteAmount = self.cost_to_precision(symbol, cost)
                elif createMarketBuyOrderRequiresPrice:
                    if price is None:
                        raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend(quote quantity) in the amount argument')
                    else:
                        amountString = self.number_to_string(amount)
                        priceString = self.number_to_string(price)
                        costRequest = Precise.string_mul(amountString, priceString)
                        quoteAmount = self.cost_to_precision(symbol, costRequest)
                else:
                    quoteAmount = self.cost_to_precision(symbol, amount)
                request['amount'] = quoteAmount
            else:
                request['quantity'] = self.amount_to_precision(symbol, amount)
        else:
            request['quantity'] = self.amount_to_precision(symbol, amount)
            request['price'] = self.price_to_precision(symbol, price)
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            request['clientOrderId'] = clientOrderId
            params = self.omit(params, 'clientOrderId')
        # remember the timestamp before issuing the request
        return [request, params]

    async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
        """
        edit a trade order
        :see: https://docs.poloniex.com/#authenticated-endpoints-orders-cancel-replace-order
        :see: https://docs.poloniex.com/#authenticated-endpoints-smart-orders-cancel-replace-order
        :param str id: order id
        :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 the currency you want to trade in units of the base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param float [params.triggerPrice]: The price at which a trigger order is triggered at
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' editOrder() does not support ' + market['type'] + ' orders, only spot orders are accepted')
        request: dict = {
            'id': id,
            # 'timeInForce': timeInForce,
        }
        triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
        request, params = self.order_request(symbol, type, side, amount, request, price, params)
        response = None
        if triggerPrice is not None:
            response = await self.privatePutSmartordersId(self.extend(request, params))
        else:
            response = await self.privatePutOrdersId(self.extend(request, params))
        #
        #     {
        #         "id" : "78923648051920896",
        #         "clientOrderId" : ""
        #     }
        #
        response = self.extend(response, {
            'side': side,
            'type': type,
        })
        return self.parse_order(response, market)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        #
        # @method
        # @name poloniex#cancelOrder
        # @description cancels an open order
        # @see https://docs.poloniex.com/#authenticated-endpoints-orders-cancel-order-by-id
        # @see https://docs.poloniex.com/#authenticated-endpoints-smart-orders-cancel-order-by-id  # trigger orders
        # @param {string} id order id
        # @param {string} symbol unified symbol of the market the order was made in
        # @param {object} [params] extra parameters specific to the exchange API endpoint
        # @param {boolean} [params.trigger] True if canceling a trigger order
        # @returns {object} An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        #
        await self.load_markets()
        request: dict = {}
        clientOrderId = self.safe_value(params, 'clientOrderId')
        if clientOrderId is not None:
            id = clientOrderId
        request['id'] = id
        isTrigger = self.safe_value_2(params, 'trigger', 'stop')
        params = self.omit(params, ['clientOrderId', 'trigger', 'stop'])
        response = None
        if isTrigger:
            response = await self.privateDeleteSmartordersId(self.extend(request, params))
        else:
            response = await self.privateDeleteOrdersId(self.extend(request, params))
        #
        #   {
        #       "orderId":"210832697138888704",
        #       "clientOrderId":"",
        #       "state":"PENDING_CANCEL",
        #       "code":200,
        #       "message":""
        #   }
        #
        return self.parse_order(response)

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders
        :see: https://docs.poloniex.com/#authenticated-endpoints-orders-cancel-all-orders
        :see: https://docs.poloniex.com/#authenticated-endpoints-smart-orders-cancel-all-orders  # trigger orders
        :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.trigger]: True if canceling trigger orders
        :returns dict[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {
            # 'accountTypes': 'SPOT',
            'symbols': [],
        }
        market: Market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbols'] = [
                market['id'],
            ]
        isTrigger = self.safe_value_2(params, 'trigger', 'stop')
        params = self.omit(params, ['trigger', 'stop'])
        response = None
        if isTrigger:
            response = await self.privateDeleteSmartorders(self.extend(request, params))
        else:
            response = await self.privateDeleteOrders(self.extend(request, params))
        #
        #     [
        #         {
        #             "orderId" : "78xxxxxxxx80",
        #             "clientOrderId" : "",
        #             "state" : "NEW",
        #             "code" : 200,
        #             "message" : ""
        #         }, {
        #             "orderId" : "78xxxxxxxxx80",
        #             "clientOrderId" : "",
        #             "state" : "NEW",
        #             "code" : 200,
        #             "message" : ""
        #         }
        #     ]
        #
        return self.parse_orders(response, market)

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetch an order by it's id
        :see: https://docs.poloniex.com/#authenticated-endpoints-orders-order-details
        :see: https://docs.poloniex.com/#authenticated-endpoints-smart-orders-open-orders  # trigger orders
        :param str id: order id
        :param str symbol: unified market symbol, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.trigger]: True if fetching a trigger order
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        id = str(id)
        request: dict = {
            'id': id,
        }
        isTrigger = self.safe_value_2(params, 'trigger', 'stop')
        params = self.omit(params, ['trigger', 'stop'])
        response = None
        if isTrigger:
            response = await self.privateGetSmartordersId(self.extend(request, params))
            response = self.safe_value(response, 0)
        else:
            response = await self.privateGetOrdersId(self.extend(request, params))
        #
        #     {
        #         "id": "21934611974062080",
        #         "clientOrderId": "123",
        #         "symbol": "TRX_USDC",
        #         "state": "NEW",
        #         "accountType": "SPOT",
        #         "side": "SELL",
        #         "type": "LIMIT",
        #         "timeInForce": "GTC",
        #         "quantity": "1.00",
        #         "price": "10.00",
        #         "avgPrice": "0.00",
        #         "amount": "0.00",
        #         "filledQuantity": "0.00",
        #         "filledAmount": "0.00",
        #         "stopPrice": "3750.00",              # for trigger orders
        #         "createTime": 1646196019020,
        #         "updateTime": 1646196019020
        #     }
        #
        order = self.parse_order(response)
        order['id'] = id
        return order

    async def fetch_order_status(self, id: str, symbol: Str = None, params={}):
        await self.load_markets()
        orders = await self.fetch_open_orders(symbol, None, None, params)
        indexed = self.index_by(orders, 'id')
        return 'open' if (id in indexed) else 'closed'

    async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all the trades made from a single order
        :see: https://docs.poloniex.com/#authenticated-endpoints-trades-trades-by-order-id
        :param str id: order id
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        request: dict = {
            'id': id,
        }
        trades = await self.privateGetOrdersIdTrades(self.extend(request, params))
        #
        #     [
        #         {
        #             "id": "30341456333942784",
        #             "symbol": "LINK_USDT",
        #             "accountType": "SPOT",
        #             "orderId": "30249408733945856",
        #             "side": "BUY",
        #             "type": "LIMIT",
        #             "matchRole": "MAKER",
        #             "createTime": 1648200366864,
        #             "price": "3.1",
        #             "quantity": "1",
        #             "amount": "3.1",
        #             "feeCurrency": "LINK",
        #             "feeAmount": "0.00145",
        #             "pageId": "30341456333942784",
        #             "clientOrderId": ""
        #         }
        #     ]
        #
        return self.parse_trades(trades)

    def parse_balance(self, response) -> Balances:
        result: dict = {
            'info': response,
            'timestamp': None,
            'datetime': None,
        }
        for i in range(0, len(response)):
            account = self.safe_value(response, i, {})
            balances = self.safe_value(account, 'balances')
            for j in range(0, len(balances)):
                balance = self.safe_value(balances, j)
                currencyId = self.safe_string(balance, 'currency')
                code = self.safe_currency_code(currencyId)
                newAccount = self.account()
                newAccount['free'] = self.safe_string(balance, 'available')
                newAccount['used'] = self.safe_string(balance, 'hold')
                result[code] = newAccount
        return self.safe_balance(result)

    async def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :see: https://docs.poloniex.com/#authenticated-endpoints-accounts-all-account-balances
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        await self.load_markets()
        request: dict = {
            'accountType': 'SPOT',
        }
        response = await self.privateGetAccountsBalances(self.extend(request, params))
        #
        #     [
        #         {
        #             "accountId" : "7xxxxxxxxxx8",
        #             "accountType" : "SPOT",
        #             "balances" : [
        #                 {
        #                     "currencyId" : "214",
        #                     "currency" : "USDT",
        #                     "available" : "2.00",
        #                     "hold" : "0.00"
        #                 }
        #             ]
        #         }
        #     ]
        #
        return self.parse_balance(response)

    async def fetch_trading_fees(self, params={}) -> TradingFees:
        """
        fetch the trading fees for multiple markets
        :see: https://docs.poloniex.com/#authenticated-endpoints-accounts-fee-info
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `fee structures <https://docs.ccxt_zagrava.com/#/?id=fee-structure>` indexed by market symbols
        """
        await self.load_markets()
        response = await self.privateGetFeeinfo(params)
        #
        #     {
        #         "trxDiscount" : False,
        #         "makerRate" : "0.00145",
        #         "takerRate" : "0.00155",
        #         "volume30D" : "0.00"
        #     }
        #
        result: dict = {}
        for i in range(0, len(self.symbols)):
            symbol = self.symbols[i]
            result[symbol] = {
                'info': response,
                'symbol': symbol,
                'maker': self.safe_number(response, 'makerRate'),
                'taker': self.safe_number(response, 'takerRate'),
                'percentage': True,
                'tierBased': True,
            }
        return result

    async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :see: https://docs.poloniex.com/#public-endpoints-market-data-order-book
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # The default value of limit is 10. Valid limit values are: 5, 10, 20, 50, 100, 150.
        response = await self.publicGetMarketsSymbolOrderBook(self.extend(request, params))
        #
        #     {
        #         "time" : 1659695219507,
        #         "scale" : "-1",
        #         "asks" : ["23139.82", "0.317981", "23140", "0.191091", "23170.06", "0.01", "23200", "0.107758", "23230.55", "0.01", "23247.2", "0.154", "23254", "0.005121", "23263", "0.038", "23285.4", "0.308", "23300", "0.108896"],
        #         "bids" : ["23139.74", "0.432092", "23139.73", "0.198592", "23123.21", "0.000886", "23123.2", "0.308", "23121.4", "0.154", "23105", "0.000789", "23100", "0.078175", "23069.1", "0.026276", "23068.83", "0.001329", "23051", "0.000048"],
        #         "ts" : 1659695219513
        #     }
        #
        timestamp = self.safe_integer(response, 'time')
        asks = self.safe_value(response, 'asks')
        bids = self.safe_value(response, 'bids')
        asksResult = []
        bidsResult = []
        for i in range(0, len(asks)):
            if (i % 2) < 1:
                price = self.safe_number(asks, i)
                amount = self.safe_number(asks, self.sum(i, 1))
                asksResult.append([price, amount])
        for i in range(0, len(bids)):
            if (i % 2) < 1:
                price = self.safe_number(bids, i)
                amount = self.safe_number(bids, self.sum(i, 1))
                bidsResult.append([price, amount])
        return {
            'symbol': market['symbol'],
            'bids': self.sort_by(bidsResult, 0, True),
            'asks': self.sort_by(asksResult, 0),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'nonce': None,
        }

    async def create_deposit_address(self, code: str, params={}):
        """
        create a currency deposit address
        :see: https://docs.poloniex.com/#authenticated-endpoints-wallets-deposit-addresses
        :param str code: unified currency code of the currency for the deposit address
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
        }
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
        if network is not None:
            request['currency'] += network  # when network the currency need to be changed to currency+network https://docs.poloniex.com/#withdraw on MultiChain Currencies section
            params = self.omit(params, 'network')
        else:
            if currency['id'] == 'USDT':
                raise ArgumentsRequired(self.id + ' createDepositAddress requires a network parameter for ' + code + '.')
        response = await self.privatePostWalletsAddress(self.extend(request, params))
        #
        #     {
        #         "address" : "0xfxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxf"
        #     }
        #
        address = self.safe_string(response, 'address')
        tag: Str = None
        self.check_address(address)
        if currency is not None:
            depositAddress = self.safe_string(currency['info'], 'depositAddress')
            if depositAddress is not None:
                tag = address
                address = depositAddress
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': network,
            'info': response,
        }

    async def fetch_deposit_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :see: https://docs.poloniex.com/#authenticated-endpoints-wallets-deposit-addresses
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
        }
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
        if network is not None:
            request['currency'] += network  # when network the currency need to be changed to currency+network https://docs.poloniex.com/#withdraw on MultiChain Currencies section
            params = self.omit(params, 'network')
        else:
            if currency['id'] == 'USDT':
                raise ArgumentsRequired(self.id + ' fetchDepositAddress requires a network parameter for ' + code + '.')
        response = await self.privateGetWalletsAddresses(self.extend(request, params))
        #
        #     {
        #         "USDTTRON" : "Txxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxp"
        #     }
        #
        address = self.safe_string(response, request['currency'])
        tag: Str = None
        self.check_address(address)
        if currency is not None:
            depositAddress = self.safe_string(currency['info'], 'depositAddress')
            if depositAddress is not None:
                tag = address
                address = depositAddress
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': network,
            'info': response,
        }

    async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account
        :see: https://docs.poloniex.com/#authenticated-endpoints-accounts-accounts-transfer
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transfer structure <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        accountsByType = self.safe_value(self.options, 'accountsByType', {})
        fromId = self.safe_string(accountsByType, fromAccount, fromAccount)
        toId = self.safe_string(accountsByType, toAccount, fromAccount)
        request: dict = {
            'amount': self.currency_to_precision(code, amount),
            'currency': currency['id'],
            'fromAccount': fromId,
            'toAccount': toId,
        }
        response = await self.privatePostAccountsTransfer(self.extend(request, params))
        #
        #    {
        #        "transferId" : "168041074"
        #    }
        #
        return self.parse_transfer(response, currency)

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        #    {
        #        "transferId" : "168041074"
        #    }
        #
        return {
            'info': transfer,
            'id': self.safe_string(transfer, 'transferId'),
            'timestamp': None,
            'datetime': None,
            'currency': self.safe_string(currency, 'id'),
            'amount': None,
            'fromAccount': None,
            'toAccount': None,
            'status': None,
        }

    async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        make a withdrawal
        :see: https://docs.poloniex.com/#authenticated-endpoints-wallets-withdraw-currency
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
            'amount': amount,
            'address': address,
        }
        if tag is not None:
            request['paymentId'] = tag
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
        if network is not None:
            request['currency'] += network  # when network the currency need to be changed to currency+network https://docs.poloniex.com/#withdraw on MultiChain Currencies section
            params = self.omit(params, 'network')
        response = await self.privatePostWalletsWithdraw(self.extend(request, params))
        #
        #     {
        #         "response": "Withdrew 1.00000000 USDT.",
        #         "email2FA": False,
        #         "withdrawalNumber": 13449869
        #     }
        #
        return self.parse_transaction(response, currency)

    async def fetch_transactions_helper(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
        await self.load_markets()
        year = 31104000  # 60 * 60 * 24 * 30 * 12 = one year of history, why not
        now = self.seconds()
        start = self.parse_to_int(since / 1000) if (since is not None) else now - 10 * year
        request: dict = {
            'start': start,  # UNIX timestamp, required
            'end': now,  # UNIX timestamp, required
        }
        response = await self.privateGetWalletsActivity(self.extend(request, params))
        #
        #     {
        #         "adjustments":[],
        #         "deposits":[
        #             {
        #                 "currency": "BTC",
        #                 "address": "1MEtiqJWru53FhhHrfJPPvd2tC3TPDVcmW",
        #                 "amount": "0.01063000",
        #                 "confirmations":  1,
        #                 "txid": "952b0e1888d6d491591facc0d37b5ebec540ac1efb241fdbc22bcc20d1822fb6",
        #                 "timestamp":  1507916888,
        #                 "status": "COMPLETE"
        #             },
        #             {
        #                 "currency": "ETH",
        #                 "address": "0x20108ba20b65c04d82909e91df06618107460197",
        #                 "amount": "4.00000000",
        #                 "confirmations": 38,
        #                 "txid": "0x4be260073491fe63935e9e0da42bd71138fdeb803732f41501015a2d46eb479d",
        #                 "timestamp": 1525060430,
        #                 "status": "COMPLETE"
        #             }
        #         ],
        #         "withdrawals":[
        #             {
        #                 "withdrawalNumber":13449869,
        #                 "currency":"USDTTRON",  # not documented in API docs, see commonCurrencies in describe()
        #                 "address":"TXGaqPW23JdRWhsVwS2mRsGsegbdnAd3Rw",
        #                 "amount":"1.00000000",
        #                 "fee":"0.00000000",
        #                 "timestamp":1591573420,
        #                 "status":"COMPLETE: dadf427224b3d44b38a2c13caa4395e4666152556ca0b2f67dbd86a95655150f",
        #                 "ipAddress":"x.x.x.x",
        #                 "canCancel":0,
        #                 "canResendEmail":0,
        #                 "paymentID":null,
        #                 "scope":"crypto"
        #             },
        #             {
        #                 "withdrawalNumber": 8224394,
        #                 "currency": "EMC2",
        #                 "address": "EYEKyCrqTNmVCpdDV8w49XvSKRP9N3EUyF",
        #                 "amount": "63.10796020",
        #                 "fee": "0.01000000",
        #                 "timestamp": 1510819838,
        #                 "status": "COMPLETE: d37354f9d02cb24d98c8c4fc17aa42f475530b5727effdf668ee5a43ce667fd6",
        #                 "ipAddress": "x.x.x.x"
        #             },
        #             {
        #                 "withdrawalNumber": 9290444,
        #                 "currency": "ETH",
        #                 "address": "0x191015ff2e75261d50433fbd05bd57e942336149",
        #                 "amount": "0.15500000",
        #                 "fee": "0.00500000",
        #                 "timestamp": 1514099289,
        #                 "status": "COMPLETE: 0x12d444493b4bca668992021fd9e54b5292b8e71d9927af1f076f554e4bea5b2d",
        #                 "ipAddress": "x.x.x.x"
        #             },
        #             {
        #                 "withdrawalNumber": 11518260,
        #                 "currency": "BTC",
        #                 "address": "8JoDXAmE1GY2LRK8jD1gmAmgRPq54kXJ4t",
        #                 "amount": "0.20000000",
        #                 "fee": "0.00050000",
        #                 "timestamp": 1527918155,
        #                 "status": "COMPLETE: 1864f4ebb277d90b0b1ff53259b36b97fa1990edc7ad2be47c5e0ab41916b5ff",
        #                 "ipAddress": "x.x.x.x"
        #             }
        #         ]
        #     }
        #
        return response

    async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch history of deposits and withdrawals
        :see: https://docs.poloniex.com/#authenticated-endpoints-wallets-wallets-activity-records
        :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
        :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
        :param int [limit]: max number of deposit/withdrawals to return, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        await self.load_markets()
        response = await self.fetch_transactions_helper(code, since, limit, params)
        currency: Currency = None
        if code is not None:
            currency = self.currency(code)
        withdrawals = self.safe_value(response, 'withdrawals', [])
        deposits = self.safe_value(response, 'deposits', [])
        withdrawalTransactions = self.parse_transactions(withdrawals, currency, since, limit)
        depositTransactions = self.parse_transactions(deposits, currency, since, limit)
        transactions = self.array_concat(depositTransactions, withdrawalTransactions)
        return self.filter_by_currency_since_limit(self.sort_by(transactions, 'timestamp'), code, since, limit)

    async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account
        :see: https://docs.poloniex.com/#authenticated-endpoints-wallets-wallets-activity-records
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch withdrawals for
        :param int [limit]: the maximum number of withdrawals structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        response = await self.fetch_transactions_helper(code, since, limit, params)
        currency: Currency = None
        if code is not None:
            currency = self.currency(code)
        withdrawals = self.safe_value(response, 'withdrawals', [])
        transactions = self.parse_transactions(withdrawals, currency, since, limit)
        return self.filter_by_currency_since_limit(transactions, code, since, limit)

    async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
        """
        fetch deposit and withdraw fees
        :see: https://docs.poloniex.com/#public-endpoints-reference-data-currency-information
        :param str[]|None codes: list of unified currency codes
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `fees structures <https://docs.ccxt_zagrava.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        response = await self.publicGetCurrencies(self.extend(params, {'includeMultiChainCurrencies': True}))
        #
        #     [
        #         {
        #             "1CR": {
        #                 "id": 1,
        #                 "name": "1CRedit",
        #                 "description": "BTC Clone",
        #                 "type": "address",
        #                 "withdrawalFee": "0.01000000",
        #                 "minConf": 10000,
        #                 "depositAddress": null,
        #                 "blockchain": "1CR",
        #                 "delisted": False,
        #                 "tradingState": "NORMAL",
        #                 "walletState": "DISABLED",
        #                 "parentChain": null,
        #                 "isMultiChain": False,
        #                 "isChildChain": False,
        #                 "childChains": []
        #             }
        #         }
        #     ]
        #
        data: dict = {}
        for i in range(0, len(response)):
            entry = response[i]
            currencies = list(entry.keys())
            currencyId = self.safe_string(currencies, 0)
            data[currencyId] = entry[currencyId]
        return self.parse_deposit_withdraw_fees(data, codes)

    def parse_deposit_withdraw_fees(self, response, codes=None, currencyIdKey=None):
        #
        #         {
        #             "1CR": {
        #                 "id": 1,
        #                 "name": "1CRedit",
        #                 "description": "BTC Clone",
        #                 "type": "address",
        #                 "withdrawalFee": "0.01000000",
        #                 "minConf": 10000,
        #                 "depositAddress": null,
        #                 "blockchain": "1CR",
        #                 "delisted": False,
        #                 "tradingState": "NORMAL",
        #                 "walletState": "DISABLED",
        #                 "parentChain": null,
        #                 "isMultiChain": False,
        #                 "isChildChain": False,
        #                 "childChains": []
        #             },
        #         }
        #
        depositWithdrawFees: dict = {}
        codes = self.market_codes(codes)
        responseKeys = list(response.keys())
        for i in range(0, len(responseKeys)):
            currencyId = responseKeys[i]
            code = self.safe_currency_code(currencyId)
            feeInfo = response[currencyId]
            if (codes is None) or (self.in_array(code, codes)):
                currency = self.currency(code)
                depositWithdrawFees[code] = self.parse_deposit_withdraw_fee(feeInfo, currency)
                childChains = self.safe_value(feeInfo, 'childChains')
                chainsLength = len(childChains)
                if chainsLength > 0:
                    for j in range(0, len(childChains)):
                        networkId = childChains[j]
                        networkId = networkId.replace(code, '')
                        networkCode = self.network_id_to_code(networkId)
                        networkInfo = self.safe_value(response, networkId)
                        networkObject: dict = {}
                        withdrawFee = self.safe_number(networkInfo, 'withdrawalFee')
                        networkObject[networkCode] = {
                            'withdraw': {
                                'fee': withdrawFee,
                                'percentage': False if (withdrawFee is not None) else None,
                            },
                            'deposit': {
                                'fee': None,
                                'percentage': None,
                            },
                        }
                        depositWithdrawFees[code]['networks'] = self.extend(depositWithdrawFees[code]['networks'], networkObject)
        return depositWithdrawFees

    def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
        depositWithdrawFee = self.deposit_withdraw_fee({})
        depositWithdrawFee['info'][currency['code']] = fee
        networkId = self.safe_string(fee, 'blockchain')
        withdrawFee = self.safe_number(fee, 'withdrawalFee')
        withdrawResult: dict = {
            'fee': withdrawFee,
            'percentage': False if (withdrawFee is not None) else None,
        }
        depositResult: dict = {
            'fee': None,
            'percentage': None,
        }
        depositWithdrawFee['withdraw'] = withdrawResult
        depositWithdrawFee['deposit'] = depositResult
        networkCode = self.network_id_to_code(networkId)
        depositWithdrawFee['networks'][networkCode] = {
            'withdraw': withdrawResult,
            'deposit': depositResult,
        }
        return depositWithdrawFee

    async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account
        :see: https://docs.poloniex.com/#authenticated-endpoints-wallets-wallets-activity-records
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch deposits for
        :param int [limit]: the maximum number of deposits structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        response = await self.fetch_transactions_helper(code, since, limit, params)
        currency = None
        if code is not None:
            currency = self.currency(code)
        deposits = self.safe_value(response, 'deposits', [])
        transactions = self.parse_transactions(deposits, currency, since, limit)
        return self.filter_by_currency_since_limit(transactions, code, since, limit)

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            'COMPLETE': 'ok',
            'COMPLETED': 'ok',
            'AWAITING APPROVAL': 'pending',
            'AWAITING_APPROVAL': 'pending',
            'PENDING': 'pending',
            'PROCESSING': 'pending',
            'COMPLETE ERROR': 'failed',
            'COMPLETE_ERROR': 'failed',
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # deposits
        #
        #     {
        #         "txid": "f49d489616911db44b740612d19464521179c76ebe9021af85b6de1e2f8d68cd",
        #         "amount": "49798.01987021",
        #         "status": "COMPLETE",
        #         "address": "DJVJZ58tJC8UeUv9Tqcdtn6uhWobouxFLT",
        #         "currency": "DOGE",
        #         "timestamp": 1524321838,
        #         "confirmations": 3371,
        #         "depositNumber": 134587098
        #     }
        #
        # withdrawals
        #
        #     {
        #         "withdrawalRequestsId": 7397527,
        #         "currency": "ETC",
        #         "address": "0x26419a62055af459d2cd69bb7392f5100b75e304",
        #         "amount": "13.19951600",
        #         "fee": "0.01000000",
        #         "timestamp": 1506010932,
        #         "status": "COMPLETED",
        #         "txid": "343346392f82ac16e8c2604f2a604b7b2382d0e9d8030f673821f8de4b5f5bk",
        #         "ipAddress": "1.2.3.4",
        #         "paymentID": null
        #     }
        #
        # withdraw
        #
        #     {
        #         "withdrawalRequestsId": 33485231
        #     }
        #
        timestamp = self.safe_timestamp(transaction, 'timestamp')
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId)
        status = self.safe_string(transaction, 'status', 'pending')
        status = self.parse_transaction_status(status)
        txid = self.safe_string(transaction, 'txid')
        type = 'withdrawal' if ('withdrawalRequestsId' in transaction) else 'deposit'
        id = self.safe_string_2(transaction, 'withdrawalRequestsId', 'depositNumber')
        address = self.safe_string(transaction, 'address')
        tag = self.safe_string(transaction, 'paymentID')
        amountString = self.safe_string(transaction, 'amount')
        feeCostString = self.safe_string(transaction, 'fee')
        if type == 'withdrawal':
            amountString = Precise.string_sub(amountString, feeCostString)
        return {
            'info': transaction,
            'id': id,
            'currency': code,
            'amount': self.parse_number(amountString),
            'network': None,
            'address': address,
            'addressTo': None,
            'addressFrom': None,
            'tag': tag,
            'tagTo': None,
            'tagFrom': None,
            'status': status,
            'type': type,
            'updated': None,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'comment': None,
            'internal': None,
            'fee': {
                'currency': code,
                'cost': self.parse_number(feeCostString),
                'rate': None,
            },
        }

    def nonce(self):
        return self.milliseconds()

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api']['rest']
        query = self.omit(params, self.extract_params(path))
        implodedPath = self.implode_params(path, params)
        if api == 'public':
            url += '/' + implodedPath
            if query:
                url += '?' + self.urlencode(query)
        else:
            self.check_required_credentials()
            timestamp = str(self.nonce())
            auth = method + "\n"  # eslint-disable-line quotes
            url += '/' + implodedPath
            auth += '/' + implodedPath
            if (method == 'POST') or (method == 'PUT') or (method == 'DELETE'):
                auth += "\n"  # eslint-disable-line quotes
                if query:
                    body = self.json(query)
                    auth += 'requestBody=' + body + '&'
                auth += 'signTimestamp=' + timestamp
            else:
                sortedQuery = self.extend({'signTimestamp': timestamp}, query)
                sortedQuery = self.keysort(sortedQuery)
                auth += "\n" + self.urlencode(sortedQuery)  # eslint-disable-line quotes
                if query:
                    url += '?' + self.urlencode(query)
            signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256, 'base64')
            headers = {
                'Content-Type': 'application/json',
                'key': self.apiKey,
                'signTimestamp': timestamp,
                'signature': signature,
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None
        #
        #     {
        #         "code" : 21709,
        #         "message" : "Low available balance"
        #     }
        #
        responseCode = self.safe_string(response, 'code')
        if (responseCode is not None) and (responseCode != '200'):
            codeInner = response['code']
            message = self.safe_string(response, 'message')
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], codeInner, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            raise ExchangeError(feedback)  # unknown message
        return None
