# -*- 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.coinone import ImplicitAPI
import hashlib
from ccxt_zagrava.base.types import Balances, Currencies, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
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 OrderNotFound
from ccxt_zagrava.base.errors import OnMaintenance
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class coinone(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(coinone, self).describe(), {
            'id': 'coinone',
            'name': 'CoinOne',
            'countries': ['KR'],  # Korea
            'rateLimit': 50,
            'version': 'v2',
            'pro': False,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelOrder': True,
                'closeAllPositions': False,
                'closePosition': False,
                'createMarketOrder': False,
                'createOrder': True,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': False,
                'createStopMarketOrder': False,
                'createStopOrder': False,
                'fetchBalance': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': False,  # the endpoint that should return closed orders actually returns trades, https://github.com/ccxt_zagrava/ccxt_zagrava/pull/7067
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddresses': True,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchMarginMode': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositionMode': False,
                'fetchPositions': False,
                'fetchPositionsForSymbol': False,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTrades': True,
                'reduceMargin': False,
                'setLeverage': False,
                'setMarginMode': False,
                'setPositionMode': False,
                'ws': True,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/38003300-adc12fba-323f-11e8-8525-725f53c4a659.jpg',
                'api': {
                    'rest': 'https://api.coinone.co.kr',
                    'v2Public': 'https://api.coinone.co.kr/public/v2',
                    'v2Private': 'https://api.coinone.co.kr/v2',
                    'v2_1Private': 'https://api.coinone.co.kr/v2.1',
                },
                'www': 'https://coinone.co.kr',
                'doc': 'https://doc.coinone.co.kr',
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'api': {
                'public': {
                    'get': [
                        'orderbook',
                        'ticker',
                        'ticker_utc',
                        'trades',
                    ],
                },
                'v2Public': {
                    'get': [
                        'range_units',
                        'markets/{quote_currency}',
                        'markets/{quote_currency}/{target_currency}',
                        'orderbook/{quote_currency}/{target_currency}',
                        'trades/{quote_currency}/{target_currency}',
                        'ticker_new/{quote_currency}',
                        'ticker_new/{quote_currency}/{target_currency}',
                        'ticker_utc_new/{quote_currency}',
                        'ticker_utc_new/{quote_currency}/{target_currency}',
                        'currencies',
                        'currencies/{currency}',
                        'chart/{quote_currency}/{target_currency}',
                    ],
                },
                'private': {
                    'post': [
                        'account/deposit_address',
                        'account/btc_deposit_address',
                        'account/balance',
                        'account/daily_balance',
                        'account/user_info',
                        'account/virtual_account',
                        'order/cancel_all',
                        'order/cancel',
                        'order/limit_buy',
                        'order/limit_sell',
                        'order/complete_orders',
                        'order/limit_orders',
                        'order/order_info',
                        'transaction/auth_number',
                        'transaction/history',
                        'transaction/krw/history',
                        'transaction/btc',
                        'transaction/coin',
                    ],
                },
                'v2Private': {
                    'post': [
                        'account/balance',
                        'account/deposit_address',
                        'account/user_info',
                        'account/virtual_account',
                        'order/cancel',
                        'order/limit_buy',
                        'order/limit_sell',
                        'order/limit_orders',
                        'order/complete_orders',
                        'order/query_order',
                        'transaction/auth_number',
                        'transaction/btc',
                        'transaction/history',
                        'transaction/krw/history',
                    ],
                },
                'v2_1Private': {
                    'post': [
                        'account/balance/all',
                        'account/balance',
                        'account/trade_fee',
                        'account/trade_fee/{quote_currency}/{target_currency}',
                        'order/limit',
                        'order/cancel',
                        'order/cancel/all',
                        'order/open_orders',
                        'order/open_orders/all',
                        'order/complete_orders',
                        'order/complete_orders/all',
                        'order/info',
                        'transaction/krw/history',
                        'transaction/coin/history',
                        'transaction/coin/withdrawal/limit',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'taker': 0.002,
                    'maker': 0.002,
                },
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                '104': OrderNotFound,
                '107': BadRequest,
                '108': BadSymbol,
                '405': OnMaintenance,
            },
            'commonCurrencies': {
                'SOC': 'Soda Coin',
            },
        })

    async def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :see: https://docs.coinone.co.kr/reference/currencies
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = await self.v2PublicGetCurrencies(params)
        #
        #     {
        #         "result": "success",
        #         "error_code": "0",
        #         "server_time": 1701054555578,
        #         "currencies": [
        #           {
        #             "name": "Polygon",
        #             "symbol": "MATIC",
        #             "deposit_status": "normal",
        #             "withdraw_status": "normal",
        #             "deposit_confirm_count": 150,
        #             "max_precision": 8,
        #             "deposit_fee": "0.0",
        #             "withdrawal_min_amount": "1.0",
        #             "withdrawal_fee": "3.0"
        #           }
        #         ]
        #     }
        #
        result: dict = {}
        currencies = self.safe_value(response, 'currencies', [])
        for i in range(0, len(currencies)):
            entry = currencies[i]
            id = self.safe_string(entry, 'symbol')
            name = self.safe_string(entry, 'name')
            code = self.safe_currency_code(id)
            withdrawStatus = self.safe_string(entry, 'withdraw_status', '')
            depositStatus = self.safe_string(entry, 'deposit_status', '')
            isWithdrawEnabled = withdrawStatus == 'normal'
            isDepositEnabled = depositStatus == 'normal'
            result[code] = {
                'id': id,
                'code': code,
                'info': entry,
                'name': name,
                'active': isWithdrawEnabled and isDepositEnabled,
                'deposit': isDepositEnabled,
                'withdraw': isWithdrawEnabled,
                'fee': self.safe_number(entry, 'withdrawal_fee'),
                'precision': self.parse_number(self.parse_precision(self.safe_string(entry, 'max_precision'))),
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': self.safe_number(entry, 'withdrawal_min_amount'),
                        'max': None,
                    },
                },
                'networks': {},
            }
        return result

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for coinone
        :see: https://docs.coinone.co.kr/v1.0/reference/tickers
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        request: dict = {
            'quote_currency': 'KRW',
        }
        response = await self.v2PublicGetTickerNewQuoteCurrency(request)
        #
        #     {
        #         "result": "success",
        #         "error_code": "0",
        #         "server_time": 1701067923060,
        #         "tickers": [
        #             {
        #                 "quote_currency": "krw",
        #                 "target_currency": "stg",
        #                 "timestamp": 1701067920001,
        #                 "high": "667.5",
        #                 "low": "667.5",
        #                 "first": "667.5",
        #                 "last": "667.5",
        #                 "quote_volume": "0.0",
        #                 "target_volume": "0.0",
        #                 "best_asks": [
        #                     {
        #                         "price": "777.0",
        #                         "qty": "73.9098"
        #                     }
        #                 ],
        #                 "best_bids": [
        #                     {
        #                         "price": "690.8",
        #                         "qty": "40.7768"
        #                     }
        #                 ],
        #                 "id": "1701067920001001"
        #             }
        #         ]
        #     }
        #
        tickers = self.safe_value(response, 'tickers', [])
        result = []
        for i in range(0, len(tickers)):
            entry = self.safe_value(tickers, i)
            id = self.safe_string(entry, 'id')
            baseId = self.safe_string_upper(entry, 'target_currency')
            quoteId = self.safe_string_upper(entry, 'quote_currency')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            result.append({
                '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': None,
                'contract': False,
                'linear': None,
                'inverse': None,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.parse_number('1e-4'),
                    'price': self.parse_number('1e-4'),
                    'cost': self.parse_number('1e-8'),
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'created': None,
                'info': entry,
            })
        return result

    def parse_balance(self, response) -> Balances:
        result: dict = {'info': response}
        balances = self.omit(response, [
            'errorCode',
            'result',
            'normalWallets',
        ])
        currencyIds = list(balances.keys())
        for i in range(0, len(currencyIds)):
            currencyId = currencyIds[i]
            balance = balances[currencyId]
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['free'] = self.safe_string(balance, 'avail')
            account['total'] = self.safe_string(balance, 'balance')
            result[code] = account
        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.coinone.co.kr/v1.0/reference/v21
        :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()
        response = await self.v2PrivatePostAccountBalance(params)
        return self.parse_balance(response)

    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.coinone.co.kr/v1.0/reference/orderbook
        :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 = {
            'quote_currency': market['quote'],
            'target_currency': market['base'],
        }
        if limit is not None:
            request['size'] = limit  # only support 5, 10, 15, 16
        response = await self.v2PublicGetOrderbookQuoteCurrencyTargetCurrency(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "error_code": "0",
        #         "timestamp": 1701071108673,
        #         "id": "1701071108673001",
        #         "quote_currency": "KRW",
        #         "target_currency": "BTC",
        #         "order_book_unit": "0.0",
        #         "bids": [
        #             {
        #                 "price": "50048000",
        #                 "qty": "0.01080229"
        #             }
        #         ],
        #         "asks": [
        #             {
        #                 "price": "50058000",
        #                 "qty": "0.00272592"
        #             }
        #         ]
        #     }
        #
        timestamp = self.safe_integer(response, 'timestamp')
        return self.parse_order_book(response, market['symbol'], timestamp, 'bids', 'asks', 'price', 'qty')

    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.coinone.co.kr/v1.0/reference/tickers
        :see: https://docs.coinone.co.kr/v1.0/reference/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)
        request: dict = {
            'quote_currency': 'KRW',
        }
        market = None
        response = None
        if symbols is not None:
            first = self.safe_string(symbols, 0)
            market = self.market(first)
            request['quote_currency'] = market['quote']
            request['target_currency'] = market['base']
            response = await self.v2PublicGetTickerNewQuoteCurrencyTargetCurrency(self.extend(request, params))
        else:
            response = await self.v2PublicGetTickerNewQuoteCurrency(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "error_code": "0",
        #         "server_time": 1701073358487,
        #         "tickers": [
        #             {
        #                 "quote_currency": "krw",
        #                 "target_currency": "btc",
        #                 "timestamp": 1701073357818,
        #                 "high": "50543000.0",
        #                 "low": "49945000.0",
        #                 "first": "50487000.0",
        #                 "last": "50062000.0",
        #                 "quote_volume": "11349804285.3859",
        #                 "target_volume": "226.07268994",
        #                 "best_asks": [
        #                     {
        #                         "price": "50081000.0",
        #                         "qty": "0.18471358"
        #                     }
        #                 ],
        #                 "best_bids": [
        #                     {
        #                         "price": "50062000.0",
        #                         "qty": "0.04213455"
        #                     }
        #                 ],
        #                 "id": "1701073357818001"
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'tickers', [])
        return self.parse_tickers(data, symbols)

    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.coinone.co.kr/v1.0/reference/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 = {
            'quote_currency': market['quote'],
            'target_currency': market['base'],
        }
        response = await self.v2PublicGetTickerNewQuoteCurrencyTargetCurrency(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "error_code": "0",
        #         "server_time": 1701073358487,
        #         "tickers": [
        #             {
        #                 "quote_currency": "krw",
        #                 "target_currency": "btc",
        #                 "timestamp": 1701073357818,
        #                 "high": "50543000.0",
        #                 "low": "49945000.0",
        #                 "first": "50487000.0",
        #                 "last": "50062000.0",
        #                 "quote_volume": "11349804285.3859",
        #                 "target_volume": "226.07268994",
        #                 "best_asks": [
        #                     {
        #                         "price": "50081000.0",
        #                         "qty": "0.18471358"
        #                     }
        #                 ],
        #                 "best_bids": [
        #                     {
        #                         "price": "50062000.0",
        #                         "qty": "0.04213455"
        #                     }
        #                 ],
        #                 "id": "1701073357818001"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'tickers', [])
        ticker = self.safe_dict(data, 0, {})
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        #     {
        #         "quote_currency": "krw",
        #         "target_currency": "btc",
        #         "timestamp": 1701073357818,
        #         "high": "50543000.0",
        #         "low": "49945000.0",
        #         "first": "50487000.0",
        #         "last": "50062000.0",
        #         "quote_volume": "11349804285.3859",
        #         "target_volume": "226.07268994",
        #         "best_asks": [
        #             {
        #                 "price": "50081000.0",
        #                 "qty": "0.18471358"
        #             }
        #         ],
        #         "best_bids": [
        #             {
        #                 "price": "50062000.0",
        #                 "qty": "0.04213455"
        #             }
        #         ],
        #         "id": "1701073357818001"
        #     }
        #
        timestamp = self.safe_integer(ticker, 'timestamp')
        last = self.safe_string(ticker, 'last')
        asks = self.safe_value(ticker, 'best_asks')
        bids = self.safe_value(ticker, 'best_bids')
        baseId = self.safe_string(ticker, 'target_currency')
        quoteId = self.safe_string(ticker, 'quote_currency')
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        return self.safe_ticker({
            'symbol': base + '/' + quote,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': self.safe_string(bids, 'price'),
            'bidVolume': self.safe_string(bids, 'qty'),
            'ask': self.safe_string(asks, 'price'),
            'askVolume': self.safe_string(asks, 'qty'),
            'vwap': None,
            'open': self.safe_string(ticker, 'first'),
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': self.safe_string(ticker, 'target_volume'),
            'quoteVolume': self.safe_string(ticker, 'quote_volume'),
            'info': ticker,
        }, market)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades(public)
        #
        #     {
        #         "id": "1701075265708001",
        #         "timestamp": 1701075265708,
        #         "price": "50020000",
        #         "qty": "0.00155177",
        #         "is_seller_maker": False
        #     }
        #
        # fetchMyTrades(private)
        #
        #     {
        #         "timestamp": "1416561032",
        #         "price": "419000.0",
        #         "type": "bid",
        #         "qty": "0.001",
        #         "feeRate": "-0.0015",
        #         "fee": "-0.0000015",
        #         "orderId": "E84A1AC2-8088-4FA0-B093-A3BCDB9B3C85"
        #     }
        #
        timestamp = self.safe_integer(trade, 'timestamp')
        market = self.safe_market(None, market)
        isSellerMaker = self.safe_value(trade, 'is_seller_maker')
        side = None
        if isSellerMaker is not None:
            side = 'sell' if isSellerMaker else 'buy'
        priceString = self.safe_string(trade, 'price')
        amountString = self.safe_string(trade, 'qty')
        orderId = self.safe_string(trade, 'orderId')
        feeCostString = self.safe_string(trade, 'fee')
        fee = None
        if feeCostString is not None:
            feeCostString = Precise.string_abs(feeCostString)
            feeRateString = self.safe_string(trade, 'feeRate')
            feeRateString = Precise.string_abs(feeRateString)
            feeCurrencyCode = market['quote'] if (side == 'sell') else market['base']
            fee = {
                'cost': feeCostString,
                'currency': feeCurrencyCode,
                'rate': feeRateString,
            }
        return self.safe_trade({
            'id': self.safe_string(trade, 'id'),
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'order': orderId,
            'symbol': market['symbol'],
            'type': None,
            'side': side,
            'takerOrMaker': None,
            'price': priceString,
            'amount': amountString,
            'cost': None,
            '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.coinone.co.kr/v1.0/reference/recent-completed-orders
        :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 = {
            'quote_currency': market['quote'],
            'target_currency': market['base'],
        }
        if limit is not None:
            request['size'] = min(limit, 200)
        response = await self.v2PublicGetTradesQuoteCurrencyTargetCurrency(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "error_code": "0",
        #         "server_time": 1701075315771,
        #         "quote_currency": "KRW",
        #         "target_currency": "BTC",
        #         "transactions": [
        #             {
        #                 "id": "1701075265708001",
        #                 "timestamp": 1701075265708,
        #                 "price": "50020000",
        #                 "qty": "0.00155177",
        #                 "is_seller_maker": False
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'transactions', [])
        return self.parse_trades(data, market, since, limit)

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://doc.coinone.co.kr/#tag/Order-V2/operation/v2_order_limit_buy
        :see: https://doc.coinone.co.kr/#tag/Order-V2/operation/v2_order_limit_sell
        :param str symbol: unified symbol of the market to create an order in
        :param str type: must be 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if type != 'limit':
            raise ExchangeError(self.id + ' createOrder() allows limit orders only')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'price': price,
            'currency': market['id'],
            'qty': amount,
        }
        method = 'privatePostOrder' + self.capitalize(type) + self.capitalize(side)
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "errorCode": "0",
        #         "orderId": "8a82c561-40b4-4cb3-9bc0-9ac9ffc1d63b"
        #     }
        #
        return self.parse_order(response, market)

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'order_id': id,
            'currency': market['id'],
        }
        response = await self.v2PrivatePostOrderQueryOrder(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "errorCode": "0",
        #         "orderId": "0e3019f2-1e4d-11e9-9ec7-00e04c3600d7",
        #         "baseCurrency": "KRW",
        #         "targetCurrency": "BTC",
        #         "price": "10011000.0",
        #         "originalQty": "3.0",
        #         "executedQty": "0.62",
        #         "canceledQty": "1.125",
        #         "remainQty": "1.255",
        #         "status": "partially_filled",
        #         "side": "bid",
        #         "orderedAt": 1499340941,
        #         "updatedAt": 1499341142,
        #         "feeRate": "0.002",
        #         "fee": "0.00124",
        #         "averageExecutedPrice": "10011000.0"
        #     }
        #
        return self.parse_order(response, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'live': 'open',
            'partially_filled': 'open',
            'partially_canceled': 'open',
            'filled': 'closed',
            'canceled': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # createOrder
        #
        #     {
        #         "result": "success",
        #         "errorCode": "0",
        #         "orderId": "8a82c561-40b4-4cb3-9bc0-9ac9ffc1d63b"
        #     }
        #
        # fetchOrder
        #
        #     {
        #         "result": "success",
        #         "errorCode": "0",
        #         "orderId": "0e3019f2-1e4d-11e9-9ec7-00e04c3600d7",
        #         "baseCurrency": "KRW",
        #         "targetCurrency": "BTC",
        #         "price": "10011000.0",
        #         "originalQty": "3.0",
        #         "executedQty": "0.62",
        #         "canceledQty": "1.125",
        #         "remainQty": "1.255",
        #         "status": "partially_filled",
        #         "side": "bid",
        #         "orderedAt": 1499340941,
        #         "updatedAt": 1499341142,
        #         "feeRate": "0.002",
        #         "fee": "0.00124",
        #         "averageExecutedPrice": "10011000.0"
        #     }
        #
        # fetchOpenOrders
        #
        #     {
        #         "index": "0",
        #         "orderId": "68665943-1eb5-4e4b-9d76-845fc54f5489",
        #         "timestamp": "1449037367",
        #         "price": "444000.0",
        #         "qty": "0.3456",
        #         "type": "ask",
        #         "feeRate": "-0.0015"
        #     }
        #
        id = self.safe_string(order, 'orderId')
        baseId = self.safe_string(order, 'baseCurrency')
        quoteId = self.safe_string(order, 'targetCurrency')
        base = None
        quote = None
        if baseId is not None:
            base = self.safe_currency_code(baseId)
        if quoteId is not None:
            quote = self.safe_currency_code(quoteId)
        symbol = None
        if (base is not None) and (quote is not None):
            symbol = base + '/' + quote
            market = self.safe_market(symbol, market, '/')
        timestamp = self.safe_timestamp_2(order, 'timestamp', 'updatedAt')
        side = self.safe_string_2(order, 'type', 'side')
        if side == 'ask':
            side = 'sell'
        elif side == 'bid':
            side = 'buy'
        remainingString = self.safe_string(order, 'remainQty')
        amountString = self.safe_string_2(order, 'originalQty', 'qty')
        status = self.safe_string(order, 'status')
        # https://github.com/ccxt_zagrava/ccxt_zagrava/pull/7067
        if status == 'live':
            if (remainingString is not None) and (amountString is not None):
                isLessThan = Precise.string_lt(remainingString, amountString)
                if isLessThan:
                    status = 'canceled'
        status = self.parse_order_status(status)
        fee = None
        feeCostString = self.safe_string(order, 'fee')
        if feeCostString is not None:
            feeCurrencyCode = quote if (side == 'sell') else base
            fee = {
                'cost': feeCostString,
                'rate': self.safe_string(order, 'feeRate'),
                'currency': feeCurrencyCode,
            }
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': symbol,
            'type': 'limit',
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': self.safe_string(order, 'price'),
            'stopPrice': None,
            'triggerPrice': None,
            'cost': None,
            'average': self.safe_string(order, 'averageExecutedPrice'),
            'amount': amountString,
            'filled': self.safe_string(order, 'executedQty'),
            'remaining': remainingString,
            'status': status,
            'fee': fee,
            'trades': None,
        }, market)

    async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        # The returned amount might not be same ordered amount. If an order is partially filled, the returned amount means the remaining amount.
        # For the same reason, the returned amount and remaining are always same, and the returned filled and cost are always zero.
        if symbol is None:
            raise ExchangeError(self.id + ' fetchOpenOrders() allows fetching closed orders with a specific symbol')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'currency': market['id'],
        }
        response = await self.privatePostOrderLimitOrders(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "errorCode": "0",
        #         "limitOrders": [
        #             {
        #                 "index": "0",
        #                 "orderId": "68665943-1eb5-4e4b-9d76-845fc54f5489",
        #                 "timestamp": "1449037367",
        #                 "price": "444000.0",
        #                 "qty": "0.3456",
        #                 "type": "ask",
        #                 "feeRate": "-0.0015"
        #             }
        #         ]
        #     }
        #
        limitOrders = self.safe_list(response, 'limitOrders', [])
        return self.parse_orders(limitOrders, 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
        :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
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'currency': market['id'],
        }
        response = await self.v2PrivatePostOrderCompleteOrders(self.extend(request, params))
        #
        # despite the name of the endpoint it returns trades which may have a duplicate orderId
        # https://github.com/ccxt_zagrava/ccxt_zagrava/pull/7067
        #
        #     {
        #         "result": "success",
        #         "errorCode": "0",
        #         "completeOrders": [
        #             {
        #                 "timestamp": "1416561032",
        #                 "price": "419000.0",
        #                 "type": "bid",
        #                 "qty": "0.001",
        #                 "feeRate": "-0.0015",
        #                 "fee": "-0.0000015",
        #                 "orderId": "E84A1AC2-8088-4FA0-B093-A3BCDB9B3C85"
        #             }
        #         ]
        #     }
        #
        completeOrders = self.safe_list(response, 'completeOrders', [])
        return self.parse_trades(completeOrders, market, since, limit)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        if symbol is None:
            # eslint-disable-next-line quotes
            raise ArgumentsRequired(self.id + " cancelOrder() requires a symbol argument. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder.")
        price = self.safe_number(params, 'price')
        qty = self.safe_number(params, 'qty')
        isAsk = self.safe_integer(params, 'is_ask')
        if (price is None) or (qty is None) or (isAsk is None):
            # eslint-disable-next-line quotes
            raise ArgumentsRequired(self.id + " cancelOrder() requires {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument.")
        await self.load_markets()
        request: dict = {
            'order_id': id,
            'price': price,
            'qty': qty,
            'is_ask': isAsk,
            'currency': self.market_id(symbol),
        }
        response = await self.v2PrivatePostOrderCancel(self.extend(request, params))
        #
        #     {
        #         "result": "success",
        #         "errorCode": "0"
        #     }
        #
        return self.safe_order(response)

    async def fetch_deposit_addresses(self, codes: Strings = None, params={}):
        """
        fetch deposit addresses for multiple currencies and chain types
        :param str[]|None codes: list of unified currency codes, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `address structures <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        await self.load_markets()
        response = await self.v2PrivatePostAccountDepositAddress(params)
        #
        #     {
        #         "result": "success",
        #         "errorCode": "0",
        #         "walletAddress": {
        #             "matic": null,
        #             "btc": "mnobqu4i6qMCJWDpf5UimRmr8JCvZ8FLcN",
        #             "xrp": null,
        #             "xrp_tag": "-1",
        #             "kava": null,
        #             "kava_memo": null,
        #         }
        #     }
        #
        walletAddress = self.safe_value(response, 'walletAddress', {})
        keys = list(walletAddress.keys())
        result: dict = {}
        for i in range(0, len(keys)):
            key = keys[i]
            value = walletAddress[key]
            if (not value) or (value == '-1'):
                continue
            parts = key.split('_')
            currencyId = self.safe_value(parts, 0)
            secondPart = self.safe_value(parts, 1)
            code = self.safe_currency_code(currencyId)
            depositAddress = self.safe_value(result, code)
            if depositAddress is None:
                depositAddress = {
                    'currency': code,
                    'address': None,
                    'tag': None,
                    'info': value,
                }
            address = self.safe_string(depositAddress, 'address', value)
            self.check_address(address)
            depositAddress['address'] = address
            depositAddress['info'] = address
            if (secondPart == 'tag' or secondPart == 'memo'):
                depositAddress['tag'] = value
                depositAddress['info'] = [address, value]
            result[code] = depositAddress
        return result

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        request = self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        url = self.urls['api']['rest'] + '/'
        if api == 'v2Public':
            url = self.urls['api']['v2Public'] + '/'
            api = 'public'
        elif api == 'v2Private':
            url = self.urls['api']['v2Private'] + '/'
        elif api == 'v2_1Private':
            url = self.urls['api']['v2_1Private'] + '/'
        if api == 'public':
            url += request
            if query:
                url += '?' + self.urlencode(query)
        else:
            self.check_required_credentials()
            url += request
            nonce = str(self.nonce())
            json = self.json(self.extend({
                'access_token': self.apiKey,
                'nonce': nonce,
            }, params))
            payload = self.string_to_base64(json)
            body = payload
            secret = self.secret.upper()
            signature = self.hmac(self.encode(payload), self.encode(secret), hashlib.sha512)
            headers = {
                'Content-Type': 'application/json',
                'X-COINONE-PAYLOAD': payload,
                'X-COINONE-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  # fallback to default error handler
        #
        #     {"result":"error","error_code":"107","error_msg":"Parameter value is wrong"}
        #     {"result":"error","error_code":"108","error_msg":"Unknown CryptoCurrency"}
        #
        errorCode = self.safe_string(response, 'error_code')
        if errorCode is not None and errorCode != '0':
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions, errorCode, feedback)
            raise ExchangeError(feedback)  # unknown message
        return None
