<?php

namespace ccxt\async;

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

use Exception; // a common import
use ccxt\async\abstract\zonda as Exchange;
use ccxt\ExchangeError;
use ccxt\BadRequest;
use ccxt\Precise;
use \React\Async;
use \React\Promise\PromiseInterface;

class zonda extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'zonda',
            'name' => 'Zonda',
            'countries' => array( 'EE' ), // Estonia
            'rateLimit' => 1000,
            'has' => array(
                'CORS' => true,
                'spot' => true,
                'margin' => false,
                'swap' => false,
                'future' => false,
                'option' => false,
                'addMargin' => false,
                'borrowCrossMargin' => false,
                'borrowIsolatedMargin' => false,
                'borrowMargin' => false,
                'cancelAllOrders' => false,
                'cancelOrder' => true,
                'cancelOrders' => false,
                'closeAllPositions' => false,
                'closePosition' => false,
                'createDepositAddress' => false,
                'createOrder' => true,
                'createReduceOnlyOrder' => false,
                'fetchAllGreeks' => false,
                'fetchBalance' => true,
                'fetchBorrowInterest' => false,
                'fetchBorrowRate' => false,
                'fetchBorrowRateHistories' => false,
                'fetchBorrowRateHistory' => false,
                'fetchBorrowRates' => false,
                'fetchBorrowRatesPerSymbol' => false,
                'fetchCrossBorrowRate' => false,
                'fetchCrossBorrowRates' => false,
                'fetchCurrencies' => false,
                'fetchDeposit' => false,
                'fetchDepositAddress' => true,
                'fetchDepositAddresses' => true,
                'fetchDepositAddressesByNetwork' => false,
                'fetchDeposits' => null,
                'fetchFundingHistory' => false,
                'fetchFundingInterval' => false,
                'fetchFundingIntervals' => false,
                'fetchFundingRate' => false,
                'fetchFundingRateHistory' => false,
                'fetchFundingRates' => false,
                'fetchGreeks' => false,
                'fetchIndexOHLCV' => false,
                'fetchIsolatedBorrowRate' => false,
                'fetchIsolatedBorrowRates' => false,
                'fetchIsolatedPositions' => false,
                'fetchLedger' => true,
                'fetchLeverage' => false,
                'fetchLeverages' => false,
                'fetchLeverageTiers' => false,
                'fetchLiquidations' => false,
                'fetchLongShortRatio' => false,
                'fetchLongShortRatioHistory' => false,
                'fetchMarginAdjustmentHistory' => false,
                'fetchMarginMode' => false,
                'fetchMarginModes' => false,
                'fetchMarketLeverageTiers' => false,
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => false,
                'fetchMarkPrice' => false,
                'fetchMarkPrices' => false,
                'fetchMyLiquidations' => false,
                'fetchMySettlementHistory' => false,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => false,
                'fetchOpenInterestHistory' => false,
                'fetchOpenInterests' => false,
                'fetchOpenOrder' => false,
                'fetchOpenOrders' => true,
                'fetchOption' => false,
                'fetchOptionChain' => false,
                'fetchOrderBook' => true,
                'fetchOrderBooks' => false,
                'fetchPosition' => false,
                'fetchPositionHistory' => false,
                'fetchPositionMode' => false,
                'fetchPositions' => false,
                'fetchPositionsForSymbol' => false,
                'fetchPositionsHistory' => false,
                'fetchPositionsRisk' => false,
                'fetchPremiumIndexOHLCV' => false,
                'fetchSettlementHistory' => false,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTime' => false,
                'fetchTrades' => true,
                'fetchTradingFee' => false,
                'fetchTradingFees' => false,
                'fetchTransactionFee' => false,
                'fetchTransactionFees' => false,
                'fetchTransactions' => null,
                'fetchTransfer' => false,
                'fetchUnderlyingAssets' => false,
                'fetchVolatilityHistory' => false,
                'fetchWithdrawal' => false,
                'fetchWithdrawals' => null,
                'reduceMargin' => false,
                'repayCrossMargin' => false,
                'repayIsolatedMargin' => false,
                'repayMargin' => false,
                'setLeverage' => false,
                'setMargin' => false,
                'setMarginMode' => false,
                'setPositionMode' => false,
                'transfer' => true,
                'withdraw' => true,
            ),
            'timeframes' => array(
                '1m' => '60',
                '3m' => '180',
                '5m' => '300',
                '15m' => '900',
                '30m' => '1800',
                '1h' => '3600',
                '2h' => '7200',
                '4h' => '14400',
                '6h' => '21600',
                '12h' => '43200',
                '1d' => '86400',
                '3d' => '259200',
                '1w' => '604800',
            ),
            'hostname' => 'zondacrypto.exchange',
            'urls' => array(
                'referral' => 'https://auth.zondaglobal.com/ref/jHlbB4mIkdS1',
                'logo' => 'https://user-images.githubusercontent.com/1294454/159202310-a0e38007-5e7c-4ba9-a32f-c8263a0291fe.jpg',
                'www' => 'https://zondaglobal.com',
                'api' => array(
                    'public' => 'https://{hostname}/API/Public',
                    'private' => 'https://{hostname}/API/Trading/tradingApi.php',
                    'v1_01Public' => 'https://api.{hostname}/rest',
                    'v1_01Private' => 'https://api.{hostname}/rest',
                ),
                'doc' => array(
                    'https://docs.zondacrypto.exchange/',
                    'https://github.com/BitBayNet/API',
                ),
                'support' => 'https://zondaglobal.com/en/helpdesk/zonda-exchange',
                'fees' => 'https://zondaglobal.com/legal/zonda-exchange/fees',
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        '{id}/all',
                        '{id}/market',
                        '{id}/orderbook',
                        '{id}/ticker',
                        '{id}/trades',
                    ),
                ),
                'private' => array(
                    'post' => array(
                        'info',
                        'trade',
                        'cancel',
                        'orderbook',
                        'orders',
                        'transfer',
                        'withdraw',
                        'history',
                        'transactions',
                    ),
                ),
                'v1_01Public' => array(
                    'get' => array(
                        'trading/ticker',
                        'trading/ticker/{symbol}',
                        'trading/stats',
                        'trading/stats/{symbol}',
                        'trading/orderbook/{symbol}',
                        'trading/transactions/{symbol}',
                        'trading/candle/history/{symbol}/{resolution}',
                    ),
                ),
                'v1_01Private' => array(
                    'get' => array(
                        'api_payments/deposits/crypto/addresses',
                        'payments/withdrawal/{detailId}',
                        'payments/deposit/{detailId}',
                        'trading/offer',
                        'trading/stop/offer',
                        'trading/config/{symbol}',
                        'trading/history/transactions',
                        'balances/BITBAY/history',
                        'balances/BITBAY/balance',
                        'fiat_cantor/rate/{baseId}/{quoteId}',
                        'fiat_cantor/history',
                        'client_payments/v2/customer/crypto/{currency}/channels/deposit',
                        'client_payments/v2/customer/crypto/{currency}/channels/withdrawal',
                        'client_payments/v2/customer/crypto/deposit/fee',
                        'client_payments/v2/customer/crypto/withdrawal/fee',
                    ),
                    'post' => array(
                        'trading/offer/{symbol}',
                        'trading/stop/offer/{symbol}',
                        'trading/config/{symbol}',
                        'balances/BITBAY/balance',
                        'balances/BITBAY/balance/transfer/{source}/{destination}',
                        'fiat_cantor/exchange',
                        'api_payments/withdrawals/crypto',
                        'api_payments/withdrawals/fiat',
                        'client_payments/v2/customer/crypto/deposit',
                        'client_payments/v2/customer/crypto/withdrawal',
                    ),
                    'delete' => array(
                        'trading/offer/{symbol}/{id}/{side}/{price}',
                        'trading/stop/offer/{symbol}/{id}/{side}/{price}',
                    ),
                    'put' => array(
                        'balances/BITBAY/balance/{id}',
                    ),
                ),
            ),
            'fees' => array(
                'trading' => array(
                    'maker' => $this->parse_number('0.0'),
                    'taker' => $this->parse_number('0.001'),
                    'percentage' => true,
                    'tierBased' => false,
                ),
                'fiat' => array(
                    'maker' => $this->parse_number('0.0030'),
                    'taker' => $this->parse_number('0.0043'),
                    'percentage' => true,
                    'tierBased' => true,
                    'tiers' => array(
                        'taker' => array(
                            array( $this->parse_number('0.0043'), $this->parse_number('0') ),
                            array( $this->parse_number('0.0042'), $this->parse_number('1250') ),
                            array( $this->parse_number('0.0041'), $this->parse_number('3750') ),
                            array( $this->parse_number('0.0040'), $this->parse_number('7500') ),
                            array( $this->parse_number('0.0039'), $this->parse_number('10000') ),
                            array( $this->parse_number('0.0038'), $this->parse_number('15000') ),
                            array( $this->parse_number('0.0037'), $this->parse_number('20000') ),
                            array( $this->parse_number('0.0036'), $this->parse_number('25000') ),
                            array( $this->parse_number('0.0035'), $this->parse_number('37500') ),
                            array( $this->parse_number('0.0034'), $this->parse_number('50000') ),
                            array( $this->parse_number('0.0033'), $this->parse_number('75000') ),
                            array( $this->parse_number('0.0032'), $this->parse_number('100000') ),
                            array( $this->parse_number('0.0031'), $this->parse_number('150000') ),
                            array( $this->parse_number('0.0030'), $this->parse_number('200000') ),
                            array( $this->parse_number('0.0029'), $this->parse_number('250000') ),
                            array( $this->parse_number('0.0028'), $this->parse_number('375000') ),
                            array( $this->parse_number('0.0027'), $this->parse_number('500000') ),
                            array( $this->parse_number('0.0026'), $this->parse_number('625000') ),
                            array( $this->parse_number('0.0025'), $this->parse_number('875000') ),
                        ),
                        'maker' => array(
                            array( $this->parse_number('0.0030'), $this->parse_number('0') ),
                            array( $this->parse_number('0.0029'), $this->parse_number('1250') ),
                            array( $this->parse_number('0.0028'), $this->parse_number('3750') ),
                            array( $this->parse_number('0.0028'), $this->parse_number('7500') ),
                            array( $this->parse_number('0.0027'), $this->parse_number('10000') ),
                            array( $this->parse_number('0.0026'), $this->parse_number('15000') ),
                            array( $this->parse_number('0.0025'), $this->parse_number('20000') ),
                            array( $this->parse_number('0.0025'), $this->parse_number('25000') ),
                            array( $this->parse_number('0.0024'), $this->parse_number('37500') ),
                            array( $this->parse_number('0.0023'), $this->parse_number('50000') ),
                            array( $this->parse_number('0.0023'), $this->parse_number('75000') ),
                            array( $this->parse_number('0.0022'), $this->parse_number('100000') ),
                            array( $this->parse_number('0.0021'), $this->parse_number('150000') ),
                            array( $this->parse_number('0.0021'), $this->parse_number('200000') ),
                            array( $this->parse_number('0.0020'), $this->parse_number('250000') ),
                            array( $this->parse_number('0.0019'), $this->parse_number('375000') ),
                            array( $this->parse_number('0.0018'), $this->parse_number('500000') ),
                            array( $this->parse_number('0.0018'), $this->parse_number('625000') ),
                            array( $this->parse_number('0.0017'), $this->parse_number('875000') ),
                        ),
                    ),
                ),
                'funding' => array(
                    'withdraw' => array(),
                ),
            ),
            'options' => array(
                'fetchTickerMethod' => 'v1_01PublicGetTradingTickerSymbol',  // or v1_01PublicGetTradingStatsSymbol
                'fetchTickersMethod' => 'v1_01PublicGetTradingTicker',       // or v1_01PublicGetTradingStats
                'fiatCurrencies' => array( 'EUR', 'USD', 'GBP', 'PLN' ),
                'transfer' => array(
                    'fillResponseFromRequest' => true,
                ),
            ),
            'features' => array(
                'spot' => array(
                    'sandbox' => false,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => true, // todo remove
                        'triggerDirection' => false,
                        'triggerPriceType' => null,
                        'stopLossPrice' => false, // todo
                        'takeProfitPrice' => false, // todo
                        'attachedStopLossTakeProfit' => null,
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => true,
                            'PO' => true,
                            'GTD' => false,
                        ),
                        'hedged' => false,
                        'trailing' => false,
                        'leverage' => false,
                        'marketBuyByCost' => true,
                        'marketBuyRequiresPrice' => false,
                        'selfTradePrevention' => false,
                        'iceberg' => false,
                    ),
                    'createOrders' => null,
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => null,
                        'daysBack' => 100000, // todo
                        'untilDays' => 100000, // todo
                        'symbolRequired' => false,
                    ),
                    'fetchOrder' => null,
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOrders' => null,
                    'fetchClosedOrders' => null, // todo
                    'fetchOHLCV' => array(
                        'limit' => null,
                    ),
                ),
                'swap' => array(
                    'linear' => null,
                    'inverse' => null,
                ),
                'future' => array(
                    'linear' => null,
                    'inverse' => null,
                ),
            ),
            'precisionMode' => TICK_SIZE,
            'exceptions' => array(
                '400' => '\\ccxt\\ExchangeError', // At least one parameter wasn't set
                '401' => '\\ccxt\\InvalidOrder', // Invalid order type
                '402' => '\\ccxt\\InvalidOrder', // No orders with specified currencies
                '403' => '\\ccxt\\InvalidOrder', // Invalid payment currency name
                '404' => '\\ccxt\\InvalidOrder', // Error. Wrong transaction type
                '405' => '\\ccxt\\InvalidOrder', // Order with this id doesn't exist
                '406' => '\\ccxt\\InsufficientFunds', // No enough money or crypto
                // code 407 not specified are not specified in their docs
                '408' => '\\ccxt\\InvalidOrder', // Invalid currency name
                '501' => '\\ccxt\\AuthenticationError', // Invalid public key
                '502' => '\\ccxt\\AuthenticationError', // Invalid sign
                '503' => '\\ccxt\\InvalidNonce', // Invalid moment parameter. Request time doesn't match current server time
                '504' => '\\ccxt\\ExchangeError', // Invalid method
                '505' => '\\ccxt\\AuthenticationError', // Key has no permission for this action
                '506' => '\\ccxt\\AccountSuspended', // Account locked. Please contact with customer service
                // codes 507 and 508 are not specified in their docs
                '509' => '\\ccxt\\ExchangeError', // The BIC/SWIFT is required for this currency
                '510' => '\\ccxt\\BadSymbol', // Invalid market name
                'FUNDS_NOT_SUFFICIENT' => '\\ccxt\\InsufficientFunds',
                'OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS' => '\\ccxt\\InvalidOrder',
                'OFFER_NOT_FOUND' => '\\ccxt\\OrderNotFound',
                'OFFER_WOULD_HAVE_BEEN_PARTIALLY_FILLED' => '\\ccxt\\OrderImmediatelyFillable',
                'ACTION_LIMIT_EXCEEDED' => '\\ccxt\\RateLimitExceeded',
                'UNDER_MAINTENANCE' => '\\ccxt\\OnMaintenance',
                'REQUEST_TIMESTAMP_TOO_OLD' => '\\ccxt\\InvalidNonce',
                'PERMISSIONS_NOT_SUFFICIENT' => '\\ccxt\\PermissionDenied',
                'INVALID_STOP_RATE' => '\\ccxt\\InvalidOrder',
                'TIMEOUT' => '\\ccxt\\ExchangeError',
                'RESPONSE_TIMEOUT' => '\\ccxt\\ExchangeError',
                'ACTION_BLOCKED' => '\\ccxt\\PermissionDenied',
                'INVALID_HASH_SIGNATURE' => '\\ccxt\\AuthenticationError',
            ),
            'commonCurrencies' => array(
                'GGC' => 'Global Game Coin',
            ),
        ));
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/ticker-1
             *
             * retrieves data on all $markets for zonda
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing market data
             */
            $response = Async\await($this->v1_01PublicGetTradingTicker ($params));
            //
            //     {
            //         "status" => "Ok",
            //         "items" => array(
            //             "BSV-USD" => array(
            //                 "market" => array(
            //                     "code" => "BSV-USD",
            //                     "first" => array( currency => "BSV", minOffer => "0.00035", scale => 8 ),
            //                     "second" => array( currency => "USD", minOffer => "5", scale => 2 )
            //                 ),
            //                 "time" => "1557569762154",
            //                 "highestBid" => "52.31",
            //                 "lowestAsk" => "62.99",
            //                 "rate" => "63",
            //                 "previousRate" => "51.21",
            //             ),
            //         ),
            //     }
            //
            $items = $this->safe_value($response, 'items', array());
            $markets = is_array($items) ? array_values($items) : array();
            return $this->parse_markets($markets);
        }) ();
    }

    public function parse_market($item): array {
        $market = $this->safe_value($item, 'market', array());
        $id = $this->safe_string($market, 'code');
        $first = $this->safe_value($market, 'first', array());
        $second = $this->safe_value($market, 'second', array());
        $baseId = $this->safe_string($first, 'currency');
        $quoteId = $this->safe_string($second, 'currency');
        $base = $this->safe_currency_code($baseId);
        $quote = $this->safe_currency_code($quoteId);
        $fees = $this->safe_value($this->fees, 'trading', array());
        $fiatCurrencies = $this->safe_value($this->options, 'fiatCurrencies', array());
        if ($this->in_array($base, $fiatCurrencies) || $this->in_array($quote, $fiatCurrencies)) {
            $fees = $this->safe_value($this->fees, 'fiat', array());
        }
        // todo => check that the limits have ben interpreted correctly
        return array(
            'id' => $id,
            'symbol' => $base . '/' . $quote,
            'base' => $base,
            'quote' => $quote,
            'settle' => null,
            'baseId' => $baseId,
            'quoteId' => $quoteId,
            'settleId' => null,
            'type' => 'spot',
            'spot' => true,
            'margin' => false,
            'swap' => false,
            'future' => false,
            'option' => false,
            'active' => null,
            'contract' => false,
            'linear' => null,
            'inverse' => null,
            'taker' => $this->safe_number($fees, 'taker'),
            'maker' => $this->safe_number($fees, 'maker'),
            'contractSize' => null,
            'expiry' => null,
            'expiryDatetime' => null,
            'optionType' => null,
            'strike' => null,
            'precision' => array(
                'amount' => $this->parse_number($this->parse_precision($this->safe_string($first, 'scale'))),
                'price' => $this->parse_number($this->parse_precision($this->safe_string($second, 'scale'))),
            ),
            'limits' => array(
                'leverage' => array(
                    'min' => null,
                    'max' => null,
                ),
                'amount' => array(
                    'min' => $this->safe_number($first, 'minOffer'),
                    'max' => null,
                ),
                'price' => array(
                    'min' => null,
                    'max' => null,
                ),
                'cost' => array(
                    'min' => null,
                    'max' => null,
                ),
            ),
            'created' => null,
            'info' => $item,
        );
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/active-orders
             *
             * fetch all unfilled currently open orders
             * @param {string} $symbol not used by zonda fetchOpenOrders
             * @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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $request = array();
            // todo pair
            $response = Async\await($this->v1_01PrivateGetTradingOffer ($this->extend($request, $params)));
            $items = $this->safe_list($response, 'items', array());
            return $this->parse_orders($items, null, $since, $limit, array( 'status' => 'open' ));
        }) ();
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        //     {
        //         "market" => "ETH-EUR",
        //         "offerType" => "Sell",
        //         "id" => "93d3657b-d616-11e9-9248-0242ac110005",
        //         "currentAmount" => "0.04",
        //         "lockedAmount" => "0.04",
        //         "rate" => "280",
        //         "startAmount" => "0.04",
        //         "time" => "1568372806924",
        //         "postOnly" => false,
        //         "hidden" => false,
        //         "mode" => "limit",
        //         "receivedAmount" => "0.0",
        //         "firstBalanceId" => "5b816c3e-437c-4e43-9bef-47814ae7ebfc",
        //         "secondBalanceId" => "ab43023b-4079-414c-b340-056e3430a3af"
        //     }
        //
        // cancelOrder
        //
        //    {
        //        status => "Ok",
        //        errors => array()
        //    }
        //
        $marketId = $this->safe_string($order, 'market');
        $symbol = $this->safe_symbol($marketId, $market, '-');
        $timestamp = $this->safe_integer($order, 'time');
        $amount = $this->safe_string($order, 'startAmount');
        $remaining = $this->safe_string($order, 'currentAmount');
        $postOnly = $this->safe_value($order, 'postOnly');
        return $this->safe_order(array(
            'id' => $this->safe_string($order, 'id'),
            'clientOrderId' => null,
            'info' => $order,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => null,
            'status' => null,
            'symbol' => $symbol,
            'type' => $this->safe_string($order, 'mode'),
            'timeInForce' => null,
            'postOnly' => $postOnly,
            'side' => $this->safe_string_lower($order, 'offerType'),
            'price' => $this->safe_string($order, 'rate'),
            'triggerPrice' => null,
            'amount' => $amount,
            'cost' => null,
            'filled' => null,
            'remaining' => $remaining,
            'average' => null,
            'fee' => null,
            'trades' => null,
        ), $market);
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/transactions-history
             *
             * fetch all trades made by the user
             * @param {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~
             */
            Async\await($this->load_markets());
            $request = array();
            if ($symbol) {
                $markets = array( $this->market_id($symbol) );
                $symbol = $this->symbol($symbol);
                $request['markets'] = $markets;
            }
            $query = array( 'query' => $this->json($this->extend($request, $params)) );
            $response = Async\await($this->v1_01PrivateGetTradingHistoryTransactions ($query));
            //
            //     {
            //         "status" => "Ok",
            //         "totalRows" => "67",
            //         "items" => array(
            //             array(
            //                 "id" => "b54659a0-51b5-42a0-80eb-2ac5357ccee2",
            //                 "market" => "BTC-EUR",
            //                 "time" => "1541697096247",
            //                 "amount" => "0.00003",
            //                 "rate" => "4341.44",
            //                 "initializedBy" => "Sell",
            //                 "wasTaker" => false,
            //                 "userAction" => "Buy",
            //                 "offerId" => "bd19804a-6f89-4a69-adb8-eb078900d006",
            //                 "commissionValue" => null
            //             ),
            //         )
            //     }
            //
            $items = $this->safe_value($response, 'items');
            $result = $this->parse_trades($items, null, $since, $limit);
            if ($symbol === null) {
                return $result;
            }
            return $this->filter_by_symbol($result, $symbol);
        }) ();
    }

    public function parse_balance($response): array {
        $balances = $this->safe_value($response, 'balances');
        if ($balances === null) {
            throw new ExchangeError($this->id . ' empty $balance $response ' . $this->json($response));
        }
        $result = array( 'info' => $response );
        for ($i = 0; $i < count($balances); $i++) {
            $balance = $balances[$i];
            $currencyId = $this->safe_string($balance, 'currency');
            $code = $this->safe_currency_code($currencyId);
            $account = $this->account();
            $account['used'] = $this->safe_string($balance, 'lockedFunds');
            $account['free'] = $this->safe_string($balance, 'availableFunds');
            $result[$code] = $account;
        }
        return $this->safe_balance($result);
    }

    public function fetch_balance($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/list-of-wallets
             *
             * query for balance and get the amount of funds available for trading or funds locked in orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
             */
            Async\await($this->load_markets());
            $response = Async\await($this->v1_01PrivateGetBalancesBITBAYBalance ($params));
            return $this->parse_balance($response);
        }) ();
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $limit, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/orderbook-2
             *
             * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
             * @param {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->v1_01PublicGetTradingOrderbookSymbol ($this->extend($request, $params)));
            //
            //     {
            //         "status":"Ok",
            //         "sell":array(
            //             array("ra":"43988.93","ca":"0.00100525","sa":"0.00100525","pa":"0.00100525","co":1),
            //             array("ra":"43988.94","ca":"0.00114136","sa":"0.00114136","pa":"0.00114136","co":1),
            //             array("ra":"43989","ca":"0.010578","sa":"0.010578","pa":"0.010578","co":1),
            //         ),
            //         "buy":array(
            //             array("ra":"42157.33","ca":"2.83147881","sa":"2.83147881","pa":"2.83147881","co":2),
            //             array("ra":"42096.0","ca":"0.00011878","sa":"0.00011878","pa":"0.00011878","co":1),
            //             array("ra":"42022.0","ca":"0.00011899","sa":"0.00011899","pa":"0.00011899","co":1),
            //         ),
            //         "timestamp":"1642299886122",
            //         "seqNo":"27641254"
            //     }
            //
            $rawBids = $this->safe_value($response, 'buy', array());
            $rawAsks = $this->safe_value($response, 'sell', array());
            $timestamp = $this->safe_integer($response, 'timestamp');
            return array(
                'symbol' => $market['symbol'],
                'bids' => $this->parse_bids_asks($rawBids, 'ra', 'ca'),
                'asks' => $this->parse_bids_asks($rawAsks, 'ra', 'ca'),
                'timestamp' => $timestamp,
                'datetime' => $this->iso8601($timestamp),
                'nonce' => $this->safe_integer($response, 'seqNo'),
            );
        }) ();
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        //
        // version 1
        //
        //    {
        //        "m" => "ETH-PLN",
        //        "h" => "13485.13",
        //        "l" => "13100.01",
        //        "v" => "126.10710939",
        //        "r24h" => "13332.72"
        //    }
        //
        // version 2
        //
        //    {
        //        "market" => array(
        //            "code" => "ADA-USDT",
        //            "first" => array(
        //                "currency" => "ADA",
        //                "minOffer" => "0.2",
        //                "scale" => "6"
        //            ),
        //            "second" => array(
        //                "currency" => "USDT",
        //                "minOffer" => "0.099",
        //                "scale" => "6"
        //            ),
        //            "amountPrecision" => "6",
        //            "pricePrecision" => "6",
        //            "ratePrecision" => "6"
        //        ),
        //        "time" => "1655812661202",
        //        "highestBid" => "0.492",
        //        "lowestAsk" => "0.499389",
        //        "rate" => "0.50588",
        //        "previousRate" => "0.504981"
        //    }
        //
        $tickerMarket = $this->safe_value($ticker, 'market');
        $marketId = $this->safe_string_2($tickerMarket, 'code', 'm');
        $market = $this->safe_market($marketId, $market);
        $timestamp = $this->safe_integer($ticker, 'time');
        $rate = $this->safe_value($ticker, 'rate');
        return $this->safe_ticker(array(
            'symbol' => $this->safe_symbol($marketId, $market),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $this->safe_string($ticker, 'h'),
            'low' => $this->safe_string($ticker, 'l'),
            'bid' => $this->safe_number($ticker, 'highestBid'),
            'bidVolume' => null,
            'ask' => $this->safe_number($ticker, 'lowestAsk'),
            'askVolume' => null,
            'vwap' => null,
            'open' => $this->safe_string($ticker, 'r24h'),
            'close' => $rate,
            'last' => $rate,
            'previousClose' => $this->safe_value($ticker, 'previousRate'),
            'change' => null,
            'percentage' => null,
            'average' => null,
            'baseVolume' => $this->safe_string($ticker, 'v'),
            'quoteVolume' => null,
            'info' => $ticker,
        ), $market);
    }

    public function fetch_ticker(string $symbol, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * v1_01PublicGetTradingTickerSymbol retrieves timestamp, datetime, bid, ask, close, last, previousClose, v1_01PublicGetTradingStatsSymbol retrieves high, low, volume and opening price of an asset
             *
             * @see https://docs.zondacrypto.exchange/reference/market-statistics
             *
             * @param {string} $symbol unified $symbol of the $market to fetch the ticker for
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->method] v1_01PublicGetTradingTickerSymbol (default) or v1_01PublicGetTradingStatsSymbol
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $method = 'v1_01PublicGetTradingTickerSymbol';
            $defaultMethod = $this->safe_string($this->options, 'fetchTickerMethod', $method);
            $fetchTickerMethod = $this->safe_string_2($params, 'method', 'fetchTickerMethod', $defaultMethod);
            $response = null;
            if ($fetchTickerMethod === $method) {
                $response = Async\await($this->v1_01PublicGetTradingTickerSymbol ($this->extend($request, $params)));
                //
                //    {
                //        "status" => "Ok",
                //        "ticker" => {
                //            "market" => array(
                //                "code" => "ADA-USDT",
                //                "first" => array(
                //                    "currency" => "ADA",
                //                    "minOffer" => "0.21",
                //                    "scale" => 6
                //                ),
                //                "second" => array(
                //                    "currency" => "USDT",
                //                    "minOffer" => "0.099",
                //                    "scale" => 6
                //                ),
                //                "amountPrecision" => 6,
                //                "pricePrecision" => 6,
                //                "ratePrecision" => 6
                //            ),
                //            "time" => "1655810976780",
                //            "highestBid" => "0.498543",
                //            "lowestAsk" => "0.50684",
                //            "rate" => "0.50588",
                //            "previousRate" => "0.504981"
                //        }
                //    }
                //
            } elseif ($fetchTickerMethod === 'v1_01PublicGetTradingStatsSymbol') {
                $response = Async\await($this->v1_01PublicGetTradingStatsSymbol ($this->extend($request, $params)));
                //
                //    {
                //        "status" => "Ok",
                //        "stats" => {
                //            "m" => "BTC-USDT",
                //            "h" => "28800",
                //            "l" => "26703.950101",
                //            "v" => "6.72932396",
                //            "r24h" => "27122.2"
                //        }
                //    }
                //
            } else {
                throw new BadRequest($this->id . ' fetchTicker $params["method"] must be "v1_01PublicGetTradingTickerSymbol" or "v1_01PublicGetTradingStatsSymbol"');
            }
            $stats = $this->safe_value_2($response, 'ticker', 'stats');
            return $this->parse_ticker($stats, $market);
        }) ();
    }

    public function fetch_tickers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * @ignore
             * v1_01PublicGetTradingTicker retrieves timestamp, datetime, bid, ask, close, last, previousClose for each market, v1_01PublicGetTradingStats retrieves high, low, volume and opening price of each market
             *
             * @see https://docs.zondacrypto.exchange/reference/market-statistics
             *
             * @param {string[]|null} $symbols unified $symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->method] v1_01PublicGetTradingTicker (default) or v1_01PublicGetTradingStats
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
             */
            Async\await($this->load_markets());
            $method = 'v1_01PublicGetTradingTicker';
            $defaultMethod = $this->safe_string($this->options, 'fetchTickersMethod', $method);
            $fetchTickersMethod = $this->safe_string_2($params, 'method', 'fetchTickersMethod', $defaultMethod);
            $response = null;
            if ($fetchTickersMethod === $method) {
                $response = Async\await($this->v1_01PublicGetTradingTicker ($params));
                //
                //    {
                //        "status" => "Ok",
                //        "items" => {
                //            "DAI-PLN" => array(
                //                "market" => array(
                //                    "code" => "DAI-PLN",
                //                    "first" => array(
                //                        "currency" => "DAI",
                //                        "minOffer" => "0.99",
                //                        "scale" => 8
                //                    ),
                //                    "second" => array(
                //                        "currency" => "PLN",
                //                        "minOffer" => "5",
                //                        "scale" => 2
                //                    ),
                //                    "amountPrecision" => 8,
                //                    "pricePrecision" => 2,
                //                    "ratePrecision" => 2
                //                ),
                //                "time" => "1655810825137",
                //                "highestBid" => "4.42",
                //                "lowestAsk" => "4.44",
                //                "rate" => "4.44",
                //                "previousRate" => "4.43"
                //            ),
                //            ...
                //        }
                //    }
                //
            } elseif ($fetchTickersMethod === 'v1_01PublicGetTradingStats') {
                $response = Async\await($this->v1_01PublicGetTradingStats ($params));
                //
                //     {
                //         "status" => "Ok",
                //         "items" => {
                //             "DAI-PLN" => array(
                //                 "m" => "DAI-PLN",
                //                 "h" => "4.41",
                //                 "l" => "4.37",
                //                 "v" => "8.71068087",
                //                 "r24h" => "4.36"
                //             ),
                //             ...
                //         }
                //     }
                //
            } else {
                throw new BadRequest($this->id . ' fetchTickers $params["method"] must be "v1_01PublicGetTradingTicker" or "v1_01PublicGetTradingStats"');
            }
            $items = $this->safe_dict($response, 'items');
            return $this->parse_tickers($items, $symbols);
        }) ();
    }

    public function fetch_ledger(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch the history of changes, actions done by the user or operations that altered the balance of the user
             *
             * @see https://docs.zondacrypto.exchange/reference/operations-history
             *
             * @param {string} [$code] unified $currency $code, default is null
             * @param {int} [$since] timestamp in ms of the earliest ledger entry, default is null
             * @param {int} [$limit] max number of ledger entries to return, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=ledger ledger structure~
             */
            $balanceCurrencies = array();
            if ($code !== null) {
                $currency = $this->currency($code);
                $balanceCurrencies[] = $currency['id'];
            }
            $request = array(
                'balanceCurrencies' => $balanceCurrencies,
            );
            if ($since !== null) {
                $request['fromTime'] = $since;
            }
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $request = $this->extend($request, $params);
            $response = Async\await($this->v1_01PrivateGetBalancesBITBAYHistory (array( 'query' => $this->json($request) )));
            $items = $response['items'];
            return $this->parse_ledger($items, null, $since, $limit);
        }) ();
    }

    public function parse_ledger_entry(array $item, ?array $currency = null): array {
        //
        //    FUNDS_MIGRATION
        //    {
        //      "historyId" => "84ea7a29-7da5-4de5-b0c0-871e83cad765",
        //      "balance" => array(
        //        "id" => "821ec166-cb88-4521-916c-f4eb44db98df",
        //        "currency" => "LTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "LTC"
        //      ),
        //      "detailId" => null,
        //      "time" => 1506128252968,
        //      "type" => "FUNDS_MIGRATION",
        //      "value" => 0.0009957,
        //      "fundsBefore" => array( "total" => 0, "available" => 0, "locked" => 0 ),
        //      "fundsAfter" => array( "total" => 0.0009957, "available" => 0.0009957, "locked" => 0 ),
        //      "change" => array( "total" => 0.0009957, "available" => 0.0009957, "locked" => 0 )
        //    }
        //
        //    CREATE_BALANCE
        //    {
        //      "historyId" => "d0fabd8d-9107-4b5e-b9a6-3cab8af70d49",
        //      "balance" => array(
        //        "id" => "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d",
        //        "currency" => "BTG",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTG"
        //      ),
        //      "detailId" => null,
        //      "time" => 1508895244751,
        //      "type" => "CREATE_BALANCE",
        //      "value" => 0,
        //      "fundsBefore" => array( "total" => null, "available" => null, "locked" => null ),
        //      "fundsAfter" => array( "total" => 0, "available" => 0, "locked" => 0 ),
        //      "change" => array( "total" => 0, "available" => 0, "locked" => 0 )
        //    }
        //
        //    BITCOIN_GOLD_FORK
        //    {
        //      "historyId" => "2b4d52d3-611c-473d-b92c-8a8d87a24e41",
        //      "balance" => array(
        //        "id" => "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d",
        //        "currency" => "BTG",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTG"
        //      ),
        //      "detailId" => null,
        //      "time" => 1508895244778,
        //      "type" => "BITCOIN_GOLD_FORK",
        //      "value" => 0.00453512,
        //      "fundsBefore" => array( "total" => 0, "available" => 0, "locked" => 0 ),
        //      "fundsAfter" => array( "total" => 0.00453512, "available" => 0.00453512, "locked" => 0 ),
        //      "change" => array( "total" => 0.00453512, "available" => 0.00453512, "locked" => 0 )
        //    }
        //
        //    ADD_FUNDS
        //    {
        //      "historyId" => "3158236d-dae5-4a5d-81af-c1fa4af340fb",
        //      "balance" => array(
        //        "id" => "3a7e7a1e-0324-49d5-8f59-298505ebd6c7",
        //        "currency" => "BTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTC"
        //      ),
        //      "detailId" => "8e83a960-e737-4380-b8bb-259d6e236faa",
        //      "time" => 1520631178816,
        //      "type" => "ADD_FUNDS",
        //      "value" => 0.628405,
        //      "fundsBefore" => array( "total" => 0.00453512, "available" => 0.00453512, "locked" => 0 ),
        //      "fundsAfter" => array( "total" => 0.63294012, "available" => 0.63294012, "locked" => 0 ),
        //      "change" => array( "total" => 0.628405, "available" => 0.628405, "locked" => 0 )
        //    }
        //
        //    TRANSACTION_PRE_LOCKING
        //    {
        //      "historyId" => "e7d19e0f-03b3-46a8-bc72-dde72cc24ead",
        //      "balance" => array(
        //        "id" => "3a7e7a1e-0324-49d5-8f59-298505ebd6c7",
        //        "currency" => "BTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTC"
        //      ),
        //      "detailId" => null,
        //      "time" => 1520706403868,
        //      "type" => "TRANSACTION_PRE_LOCKING",
        //      "value" => -0.1,
        //      "fundsBefore" => array( "total" => 0.63294012, "available" => 0.63294012, "locked" => 0 ),
        //      "fundsAfter" => array( "total" => 0.63294012, "available" => 0.53294012, "locked" => 0.1 ),
        //      "change" => array( "total" => 0, "available" => -0.1, "locked" => 0.1 )
        //    }
        //
        //    TRANSACTION_POST_OUTCOME
        //    {
        //      "historyId" => "c4010825-231d-4a9c-8e46-37cde1f7b63c",
        //      "balance" => array(
        //        "id" => "3a7e7a1e-0324-49d5-8f59-298505ebd6c7",
        //        "currency" => "BTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTC"
        //      ),
        //      "detailId" => "bf2876bc-b545-4503-96c8-ef4de8233876",
        //      "time" => 1520706404032,
        //      "type" => "TRANSACTION_POST_OUTCOME",
        //      "value" => -0.01771415,
        //      "fundsBefore" => array( "total" => 0.63294012, "available" => 0.53294012, "locked" => 0.1 ),
        //      "fundsAfter" => array( "total" => 0.61522597, "available" => 0.53294012, "locked" => 0.08228585 ),
        //      "change" => array( "total" => -0.01771415, "available" => 0, "locked" => -0.01771415 )
        //    }
        //
        //    TRANSACTION_POST_INCOME
        //    {
        //      "historyId" => "7f18b7af-b676-4125-84fd-042e683046f6",
        //      "balance" => array(
        //        "id" => "ab43023b-4079-414c-b340-056e3430a3af",
        //        "currency" => "EUR",
        //        "type" => "FIAT",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "EUR"
        //      ),
        //      "detailId" => "f5fcb274-0cc7-4385-b2d3-bae2756e701f",
        //      "time" => 1520706404035,
        //      "type" => "TRANSACTION_POST_INCOME",
        //      "value" => 628.78,
        //      "fundsBefore" => array( "total" => 0, "available" => 0, "locked" => 0 ),
        //      "fundsAfter" => array( "total" => 628.78, "available" => 628.78, "locked" => 0 ),
        //      "change" => array( "total" => 628.78, "available" => 628.78, "locked" => 0 )
        //    }
        //
        //    TRANSACTION_COMMISSION_OUTCOME
        //    {
        //      "historyId" => "843177fa-61bc-4cbf-8be5-b029d856c93b",
        //      "balance" => array(
        //        "id" => "ab43023b-4079-414c-b340-056e3430a3af",
        //        "currency" => "EUR",
        //        "type" => "FIAT",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "EUR"
        //      ),
        //      "detailId" => "f5fcb274-0cc7-4385-b2d3-bae2756e701f",
        //      "time" => 1520706404050,
        //      "type" => "TRANSACTION_COMMISSION_OUTCOME",
        //      "value" => -2.71,
        //      "fundsBefore" => array( "total" => 766.06, "available" => 766.06, "locked" => 0 ),
        //      "fundsAfter" => array( "total" => 763.35,"available" => 763.35, "locked" => 0 ),
        //      "change" => array( "total" => -2.71, "available" => -2.71, "locked" => 0 )
        //    }
        //
        //    TRANSACTION_OFFER_COMPLETED_RETURN
        //    {
        //      "historyId" => "cac69b04-c518-4dc5-9d86-e76e91f2e1d2",
        //      "balance" => array(
        //        "id" => "3a7e7a1e-0324-49d5-8f59-298505ebd6c7",
        //        "currency" => "BTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTC"
        //      ),
        //      "detailId" => null,
        //      "time" => 1520714886425,
        //      "type" => "TRANSACTION_OFFER_COMPLETED_RETURN",
        //      "value" => 0.00000196,
        //      "fundsBefore" => array( "total" => 0.00941208, "available" => 0.00941012, "locked" => 0.00000196 ),
        //      "fundsAfter" => array( "total" => 0.00941208, "available" => 0.00941208, "locked" => 0 ),
        //      "change" => array( "total" => 0, "available" => 0.00000196, "locked" => -0.00000196 )
        //    }
        //
        //    WITHDRAWAL_LOCK_FUNDS
        //    {
        //      "historyId" => "03de2271-66ab-4960-a786-87ab9551fc14",
        //      "balance" => array(
        //        "id" => "3a7e7a1e-0324-49d5-8f59-298505ebd6c7",
        //        "currency" => "BTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTC"
        //      ),
        //      "detailId" => "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6",
        //      "time" => 1522245654481,
        //      "type" => "WITHDRAWAL_LOCK_FUNDS",
        //      "value" => -0.8,
        //      "fundsBefore" => array( "total" => 0.8, "available" => 0.8, "locked" => 0 ),
        //      "fundsAfter" => array( "total" => 0.8, "available" => 0, "locked" => 0.8 ),
        //      "change" => array( "total" => 0, "available" => -0.8, "locked" => 0.8 )
        //    }
        //
        //    WITHDRAWAL_SUBTRACT_FUNDS
        //    {
        //      "historyId" => "b0308c89-5288-438d-a306-c6448b1a266d",
        //      "balance" => array(
        //        "id" => "3a7e7a1e-0324-49d5-8f59-298505ebd6c7",
        //        "currency" => "BTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTC"
        //      ),
        //      "detailId" => "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6",
        //      "time" => 1522246526186,
        //      "type" => "WITHDRAWAL_SUBTRACT_FUNDS",
        //      "value" => -0.8,
        //      "fundsBefore" => array( "total" => 0.8, "available" => 0, "locked" => 0.8 ),
        //      "fundsAfter" => array( "total" => 0, "available" => 0, "locked" => 0 ),
        //      "change" => array( "total" => -0.8, "available" => 0, "locked" => -0.8 )
        //    }
        //
        //    TRANSACTION_OFFER_ABORTED_RETURN
        //    {
        //      "historyId" => "b1a3c075-d403-4e05-8f32-40512cdd88c0",
        //      "balance" => array(
        //        "id" => "3a7e7a1e-0324-49d5-8f59-298505ebd6c7",
        //        "currency" => "BTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTC"
        //      ),
        //      "detailId" => null,
        //      "time" => 1522512298662,
        //      "type" => "TRANSACTION_OFFER_ABORTED_RETURN",
        //      "value" => 0.0564931,
        //      "fundsBefore" => array( "total" => 0.44951311, "available" => 0.39302001, "locked" => 0.0564931 ),
        //      "fundsAfter" => array( "total" => 0.44951311, "available" => 0.44951311, "locked" => 0 ),
        //      "change" => array( "total" => 0, "available" => 0.0564931, "locked" => -0.0564931 )
        //    }
        //
        //    WITHDRAWAL_UNLOCK_FUNDS
        //    {
        //      "historyId" => "0ed569a2-c330-482e-bb89-4cb553fb5b11",
        //      "balance" => array(
        //        "id" => "3a7e7a1e-0324-49d5-8f59-298505ebd6c7",
        //        "currency" => "BTC",
        //        "type" => "CRYPTO",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "BTC"
        //      ),
        //      "detailId" => "0c7be256-c336-4111-bee7-4eb22e339700",
        //      "time" => 1527866360785,
        //      "type" => "WITHDRAWAL_UNLOCK_FUNDS",
        //      "value" => 0.05045,
        //      "fundsBefore" => array( "total" => 0.86001578, "available" => 0.80956578, "locked" => 0.05045 ),
        //      "fundsAfter" => array( "total" => 0.86001578, "available" => 0.86001578, "locked" => 0 ),
        //      "change" => array( "total" => 0, "available" => 0.05045, "locked" => -0.05045 )
        //    }
        //
        //    TRANSACTION_COMMISSION_RETURN
        //    {
        //      "historyId" => "07c89c27-46f1-4d7a-8518-b73798bf168a",
        //      "balance" => array(
        //        "id" => "ab43023b-4079-414c-b340-056e3430a3af",
        //        "currency" => "EUR",
        //        "type" => "FIAT",
        //        "userId" => "a34d361d-7bad-49c1-888e-62473b75d877",
        //        "name" => "EUR"
        //      ),
        //      "detailId" => null,
        //      "time" => 1528304043063,
        //      "type" => "TRANSACTION_COMMISSION_RETURN",
        //      "value" => 0.6,
        //      "fundsBefore" => array( "total" => 0, "available" => 0, "locked" => 0 ),
        //      "fundsAfter" => array( "total" => 0.6, "available" => 0.6, "locked" => 0 ),
        //      "change" => array( "total" => 0.6, "available" => 0.6, "locked" => 0 )
        //    }
        //
        $timestamp = $this->safe_integer($item, 'time');
        $balance = $this->safe_value($item, 'balance', array());
        $currencyId = $this->safe_string($balance, 'currency');
        $currency = $this->safe_currency($currencyId, $currency);
        $change = $this->safe_value($item, 'change', array());
        $amount = $this->safe_string($change, 'total');
        $direction = 'in';
        if (Precise::string_lt($amount, '0')) {
            $direction = 'out';
            $amount = Precise::string_neg($amount);
        }
        // there are 2 undocumented api calls => (v1_01PrivateGetPaymentsDepositDetailId and v1_01PrivateGetPaymentsWithdrawalDetailId)
        // that can be used to enrich the transfers with txid, address etc (you need to use info.detailId parameter)
        $fundsBefore = $this->safe_value($item, 'fundsBefore', array());
        $fundsAfter = $this->safe_value($item, 'fundsAfter', array());
        return $this->safe_ledger_entry(array(
            'info' => $item,
            'id' => $this->safe_string($item, 'historyId'),
            'direction' => $direction,
            'account' => null,
            'referenceId' => $this->safe_string($item, 'detailId'),
            'referenceAccount' => null,
            'type' => $this->parse_ledger_entry_type($this->safe_string($item, 'type')),
            'currency' => $this->safe_currency_code($currencyId),
            'amount' => $this->parse_number($amount),
            'before' => $this->safe_number($fundsBefore, 'total'),
            'after' => $this->safe_number($fundsAfter, 'total'),
            'status' => 'ok',
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'fee' => null,
        ), $currency);
    }

    public function parse_ledger_entry_type($type) {
        $types = array(
            'ADD_FUNDS' => 'transaction',
            'BITCOIN_GOLD_FORK' => 'transaction',
            'CREATE_BALANCE' => 'transaction',
            'FUNDS_MIGRATION' => 'transaction',
            'WITHDRAWAL_LOCK_FUNDS' => 'transaction',
            'WITHDRAWAL_SUBTRACT_FUNDS' => 'transaction',
            'WITHDRAWAL_UNLOCK_FUNDS' => 'transaction',
            'TRANSACTION_COMMISSION_OUTCOME' => 'fee',
            'TRANSACTION_COMMISSION_RETURN' => 'fee',
            'TRANSACTION_OFFER_ABORTED_RETURN' => 'trade',
            'TRANSACTION_OFFER_COMPLETED_RETURN' => 'trade',
            'TRANSACTION_POST_INCOME' => 'trade',
            'TRANSACTION_POST_OUTCOME' => 'trade',
            'TRANSACTION_PRE_LOCKING' => 'trade',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        //     array(
        //         "1582399800000",
        //         {
        //             "o" => "0.0001428",
        //             "c" => "0.0001428",
        //             "h" => "0.0001428",
        //             "l" => "0.0001428",
        //             "v" => "4",
        //             "co" => "1"
        //         }
        //     )
        //
        $first = $this->safe_value($ohlcv, 1, array());
        return array(
            $this->safe_integer($ohlcv, 0),
            $this->safe_number($first, 'o'),
            $this->safe_number($first, 'h'),
            $this->safe_number($first, 'l'),
            $this->safe_number($first, 'c'),
            $this->safe_number($first, 'v'),
        );
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/candles-chart
             *
             * fetches historical candlestick data containing the open, high, low, and close price, and the volume of a $market
             * @param {string} $symbol unified $symbol of the $market to fetch OHLCV data for
             * @param {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {int[][]} A list of candles ordered, open, high, low, close, volume
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $tradingSymbol = $market['baseId'] . '-' . $market['quoteId'];
            $request = array(
                'symbol' => $tradingSymbol,
                'resolution' => $this->safe_string($this->timeframes, $timeframe, $timeframe),
                // 'from' => 1574709092000, // unix timestamp in milliseconds, required
                // 'to' => 1574709092000, // unix timestamp in milliseconds, required
            );
            if ($limit === null) {
                $limit = 100;
            } else {
                $limit = min ($limit, 11000); // supports up to 11k candles diapason
            }
            $duration = $this->parse_timeframe($timeframe);
            $timerange = $limit * $duration * 1000;
            if ($since === null) {
                $request['to'] = $this->milliseconds();
                $request['from'] = $request['to'] - $timerange;
            } else {
                $request['from'] = $since;
                $request['to'] = $this->sum($request['from'], $timerange);
            }
            $response = Async\await($this->v1_01PublicGetTradingCandleHistorySymbolResolution ($this->extend($request, $params)));
            //
            //     {
            //         "status":"Ok",
            //         "items":[
            //             ["1591503060000",array("o":"0.02509572","c":"0.02509438","h":"0.02509664","l":"0.02509438","v":"0.02082165","co":"17")],
            //             ["1591503120000",array("o":"0.02509606","c":"0.02509515","h":"0.02509606","l":"0.02509487","v":"0.04971703","co":"13")],
            //             ["1591503180000",array("o":"0.02509532","c":"0.02509589","h":"0.02509589","l":"0.02509454","v":"0.01332236","co":"7")],
            //         ]
            //     }
            //
            $items = $this->safe_list($response, 'items', array());
            return $this->parse_ohlcvs($items, $market, $timeframe, $since, $limit);
        }) ();
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        //
        // createOrder trades
        //
        //     {
        //         "rate" => "0.02195928",
        //         "amount" => "0.00167952"
        //     }
        //
        // fetchMyTrades (private)
        //
        //     {
        //         "amount" => "0.29285199",
        //         "commissionValue" => "0.00125927",
        //         "id" => "11c8203a-a267-11e9-b698-0242ac110007",
        //         "initializedBy" => "Buy",
        //         "market" => "ETH-EUR",
        //         "offerId" => "11c82038-a267-11e9-b698-0242ac110007",
        //         "rate" => "277",
        //         "time" => "1562689917517",
        //         "userAction" => "Buy",
        //         "wasTaker" => true,
        //     }
        //
        // fetchTrades (public)
        //
        //     {
        //          "id" => "df00b0da-e5e0-11e9-8c19-0242ac11000a",
        //          "t" => "1570108958831",
        //          "a" => "0.04776653",
        //          "r" => "0.02145854",
        //          "ty" => "Sell"
        //     }
        //
        $timestamp = $this->safe_integer_2($trade, 'time', 't');
        $side = $this->safe_string_lower_2($trade, 'userAction', 'ty');
        $wasTaker = $this->safe_value($trade, 'wasTaker');
        $takerOrMaker = null;
        if ($wasTaker !== null) {
            $takerOrMaker = $wasTaker ? 'taker' : 'maker';
        }
        $priceString = $this->safe_string_2($trade, 'rate', 'r');
        $amountString = $this->safe_string_2($trade, 'amount', 'a');
        $feeCostString = $this->safe_string($trade, 'commissionValue');
        $marketId = $this->safe_string($trade, 'market');
        $market = $this->safe_market($marketId, $market, '-');
        $symbol = $market['symbol'];
        $fee = null;
        if ($feeCostString !== null) {
            $feeCurrency = ($side === 'buy') ? $market['base'] : $market['quote'];
            $fee = array(
                'currency' => $feeCurrency,
                'cost' => $feeCostString,
            );
        }
        $order = $this->safe_string($trade, 'offerId');
        // todo => check this logic
        $type = null;
        if ($order !== null) {
            $type = $order ? 'limit' : 'market';
        }
        return $this->safe_trade(array(
            'id' => $this->safe_string($trade, 'id'),
            'order' => $order,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $symbol,
            'type' => $type,
            'side' => $side,
            'price' => $priceString,
            'amount' => $amountString,
            'cost' => null,
            'takerOrMaker' => $takerOrMaker,
            'fee' => $fee,
            'info' => $trade,
        ), $market);
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/last-transactions
             *
             * get the list of most recent trades for a particular $symbol
             * @param {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $tradingSymbol = $market['baseId'] . '-' . $market['quoteId'];
            $request = array(
                'symbol' => $tradingSymbol,
            );
            if ($since !== null) {
                $request['fromTime'] = $since - 1; // result does not include exactly `$since` time therefore decrease by 1
            }
            if ($limit !== null) {
                $request['limit'] = $limit; // default - 10, max - 300
            }
            $response = Async\await($this->v1_01PublicGetTradingTransactionsSymbol ($this->extend($request, $params)));
            $items = $this->safe_list($response, 'items');
            return $this->parse_trades($items, $market, $since, $limit);
        }) ();
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade order
             *
             * @see https://docs.zondacrypto.exchange/reference/new-order
             *
             * @param {string} $symbol unified $symbol of the $market to create an order in
             * @param {string} $type 'market' or 'limit'
             * @param {string} $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 {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $tradingSymbol = $market['baseId'] . '-' . $market['quoteId'];
            $amount = floatval($this->amount_to_precision($symbol, $amount));
            $request = array(
                'symbol' => $tradingSymbol,
                'offerType' => strtoupper($side),
                'amount' => $amount,
            );
            $stopLossPrice = $this->safe_value_2($params, 'stopPrice', 'stopLossPrice');
            $isStopLossPrice = $stopLossPrice !== null;
            $isLimitOrder = $type === 'limit';
            $isMarketOrder = $type === 'market';
            $isStopLimit = ($type === 'stop-limit') || ($isLimitOrder && $isStopLossPrice);
            $isStopMarket = $type === 'stop-market' || ($isMarketOrder && $isStopLossPrice);
            $isStopOrder = $isStopLimit || $isStopMarket;
            if ($isLimitOrder || $isStopLimit) {
                $request['rate'] = $this->price_to_precision($symbol, $price);
                $request['mode'] = $isStopLimit ? 'stop-limit' : 'limit';
            } elseif ($isMarketOrder || $isStopMarket) {
                $request['mode'] = $isStopMarket ? 'stop-market' : 'market';
            } else {
                throw new ExchangeError($this->id . ' createOrder() invalid type');
            }
            $params = $this->omit($params, array( 'stopPrice', 'stopLossPrice' ));
            $response = null;
            if ($isStopOrder) {
                if (!$isStopLossPrice) {
                    throw new ExchangeError($this->id . ' createOrder() zonda requires `triggerPrice` parameter for stop-limit or stop-$market orders');
                }
                $request['stopRate'] = $this->price_to_precision($symbol, $stopLossPrice);
                $response = Async\await($this->v1_01PrivatePostTradingStopOfferSymbol ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->v1_01PrivatePostTradingOfferSymbol ($this->extend($request, $params)));
            }
            //
            // unfilled (open order)
            //
            //     {
            //         "status" => "Ok",
            //         "completed" => false, // can deduce $status from here
            //         "offerId" => "ce9cc72e-d61c-11e9-9248-0242ac110005",
            //         "transactions" => array(), // can deduce order info from here
            //     }
            //
            // filled (closed order)
            //
            //     {
            //         "status" => "Ok",
            //         "offerId" => "942a4a3e-e922-11e9-8c19-0242ac11000a",
            //         "completed" => true,
            //         "transactions" => array(
            //           array(
            //             "rate" => "0.02195928",
            //             "amount" => "0.00167952"
            //           ),
            //           array(
            //             "rate" => "0.02195928",
            //             "amount" => "0.00167952"
            //           ),
            //           {
            //             "rate" => "0.02196207",
            //             "amount" => "0.27704177"
            //           }
            //         )
            //     }
            //
            // partially-filled (open order)
            //
            //     {
            //         "status" => "Ok",
            //         "offerId" => "d0ebefab-f4d7-11e9-8c19-0242ac11000a",
            //         "completed" => false,
            //         "transactions" => array(
            //           array(
            //             "rate" => "0.02106404",
            //             "amount" => "0.0019625"
            //           ),
            //           array(
            //             "rate" => "0.02106404",
            //             "amount" => "0.0019625"
            //           ),
            //           {
            //             "rate" => "0.02105901",
            //             "amount" => "0.00975256"
            //           }
            //         )
            //     }
            //
            $id = $this->safe_string_2($response, 'offerId', 'stopOfferId');
            $completed = $this->safe_bool($response, 'completed', false);
            $status = $completed ? 'closed' : 'open';
            $transactions = $this->safe_value($response, 'transactions');
            return $this->safe_order(array(
                'id' => $id,
                'info' => $response,
                'timestamp' => null,
                'datetime' => null,
                'lastTradeTimestamp' => null,
                'status' => $status,
                'symbol' => $symbol,
                'type' => $type,
                'side' => $side,
                'price' => $price,
                'amount' => $amount,
                'cost' => null,
                'filled' => null,
                'remaining' => null,
                'average' => null,
                'fee' => null,
                'trades' => $transactions,
                'clientOrderId' => null,
            ));
        }) ();
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/cancel-order
             *
             * cancels an open order
             * @param {string} $id order $id
             * @param {string} $symbol unified $symbol of the $market the order was made in
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            $side = $this->safe_string($params, 'side');
            if ($side === null) {
                throw new ExchangeError($this->id . ' cancelOrder() requires a `$side` parameter ("buy" or "sell")');
            }
            $price = $this->safe_value($params, 'price');
            if ($price === null) {
                throw new ExchangeError($this->id . ' cancelOrder() requires a `$price` parameter (float or string)');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $tradingSymbol = $market['baseId'] . '-' . $market['quoteId'];
            $request = array(
                'symbol' => $tradingSymbol,
                'id' => $id,
                'side' => $side,
                'price' => $price,
            );
            $response = Async\await($this->v1_01PrivateDeleteTradingOfferSymbolIdSidePrice ($this->extend($request, $params)));
            // array( status => "Fail", errors => array( "NOT_RECOGNIZED_OFFER_TYPE" ) )  -- if required $params are missing
            // array( status => "Ok", errors => array() )
            return $this->parse_order($response);
        }) ();
    }

    public function is_fiat(string $currency): bool {
        $fiatCurrencies = array(
            'USD' => true,
            'EUR' => true,
            'PLN' => true,
        );
        return $this->safe_bool($fiatCurrencies, $currency, false);
    }

    public function parse_deposit_address($depositAddress, ?array $currency = null): array {
        //
        //     {
        //         "address" => "33u5YAEhQbYfjHHPsfMfCoSdEjfwYjVcBE",
        //         "currency" => "BTC",
        //         "balanceId" => "5d5d19e7-2265-49c7-af9a-047bcf384f21",
        //         "balanceEngine" => "BITBAY",
        //         "tag" => null
        //     }
        //
        $currencyId = $this->safe_string($depositAddress, 'currency');
        $address = $this->safe_string($depositAddress, 'address');
        $this->check_address($address);
        return array(
            'info' => $depositAddress,
            'currency' => $this->safe_currency_code($currencyId, $currency),
            'network' => null,
            'address' => $address,
            'tag' => $this->safe_string($depositAddress, 'tag'),
        );
    }

    public function fetch_deposit_address(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/deposit-addresses-for-crypto
             *
             * fetch the deposit address for a $currency associated with this account
             * @param {string} $code unified $currency $code
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->walletId] Wallet id to filter deposit adresses.
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'currency' => $currency['id'],
            );
            $response = Async\await($this->v1_01PrivateGetApiPaymentsDepositsCryptoAddresses ($this->extend($request, $params)));
            //
            //     {
            //         "status" => "Ok",
            //         "data" => [{
            //                 "address" => "33u5YAEhQbYfjHHPsfMfCoSdEjfwYjVcBE",
            //                 "currency" => "BTC",
            //                 "balanceId" => "5d5d19e7-2265-49c7-af9a-047bcf384f21",
            //                 "balanceEngine" => "BITBAY",
            //                 "tag" => null
            //             }
            //         ]
            //     }
            //
            $data = $this->safe_value($response, 'data');
            $first = $this->safe_dict($data, 0);
            return $this->parse_deposit_address($first, $currency);
        }) ();
    }

    public function fetch_deposit_addresses(?array $codes = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($codes, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/deposit-addresses-for-crypto
             *
             * fetch deposit addresses for multiple currencies and chain types
             * @param {string[]|null} $codes zonda does not support filtering filtering by multiple $codes and will ignore this parameter.
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a list of ~@link https://docs.ccxt.com/#/?id=address-structure address structures~
             */
            Async\await($this->load_markets());
            $response = Async\await($this->v1_01PrivateGetApiPaymentsDepositsCryptoAddresses ($params));
            //
            //     {
            //         "status" => "Ok",
            //         "data" => [{
            //                 "address" => "33u5YAEhQbYfjHHPsfMfCoSdEjfwYjVcBE",
            //                 "currency" => "BTC",
            //                 "balanceId" => "5d5d19e7-2265-49c7-af9a-047bcf384f21",
            //                 "balanceEngine" => "BITBAY",
            //                 "tag" => null
            //             }
            //         ]
            //     }
            //
            $data = $this->safe_list($response, 'data');
            return $this->parse_deposit_addresses($data, $codes);
        }) ();
    }

    public function transfer(string $code, float $amount, string $fromAccount, string $toAccount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $fromAccount, $toAccount, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/internal-$transfer
             *
             * $transfer $currency internally between wallets on the same account
             * @param {string} $code unified $currency $code
             * @param {float} $amount amount to $transfer
             * @param {string} $fromAccount account to $transfer from
             * @param {string} $toAccount account to $transfer to
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=$transfer-structure $transfer structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'source' => $fromAccount,
                'destination' => $toAccount,
                'currency' => $code,
                'funds' => $this->currency_to_precision($code, $amount),
            );
            $response = Async\await($this->v1_01PrivatePostBalancesBITBAYBalanceTransferSourceDestination ($this->extend($request, $params)));
            //
            //     {
            //         "status" => "Ok",
            //         "from" => array(
            //             "id" => "ad9397c5-3bd9-4372-82ba-22da6a90cb56",
            //             "userId" => "4bc43956-423f-47fd-9faa-acd37c58ed9f",
            //             "availableFunds" => 0.01803472,
            //             "totalFunds" => 0.01804161,
            //             "lockedFunds" => 0.00000689,
            //             "currency" => "BTC",
            //             "type" => "CRYPTO",
            //             "name" => "BTC",
            //             "balanceEngine" => "BITBAY"
            //         ),
            //         "to" => array(
            //             "id" => "01931d52-536b-4ca5-a9f4-be28c86d0cc3",
            //             "userId" => "4bc43956-423f-47fd-9faa-acd37c58ed9f",
            //             "availableFunds" => 0.0001,
            //             "totalFunds" => 0.0001,
            //             "lockedFunds" => 0,
            //             "currency" => "BTC",
            //             "type" => "CRYPTO",
            //             "name" => "Prowizja",
            //             "balanceEngine" => "BITBAY"
            //         ),
            //         "errors" => null
            //     }
            //
            $transfer = $this->parse_transfer($response, $currency);
            $transferOptions = $this->safe_value($this->options, 'transfer', array());
            $fillResponseFromRequest = $this->safe_bool($transferOptions, 'fillResponseFromRequest', true);
            if ($fillResponseFromRequest) {
                $transfer['amount'] = $amount;
            }
            return $transfer;
        }) ();
    }

    public function parse_transfer(array $transfer, ?array $currency = null): array {
        //
        //     {
        //         "status" => "Ok",
        //         "from" => array(
        //             "id" => "ad9397c5-3bd9-4372-82ba-22da6a90cb56",
        //             "userId" => "4bc43956-423f-47fd-9faa-acd37c58ed9f",
        //             "availableFunds" => 0.01803472,
        //             "totalFunds" => 0.01804161,
        //             "lockedFunds" => 0.00000689,
        //             "currency" => "BTC",
        //             "type" => "CRYPTO",
        //             "name" => "BTC",
        //             "balanceEngine" => "BITBAY"
        //         ),
        //         "to" => array(
        //             "id" => "01931d52-536b-4ca5-a9f4-be28c86d0cc3",
        //             "userId" => "4bc43956-423f-47fd-9faa-acd37c58ed9f",
        //             "availableFunds" => 0.0001,
        //             "totalFunds" => 0.0001,
        //             "lockedFunds" => 0,
        //             "currency" => "BTC",
        //             "type" => "CRYPTO",
        //             "name" => "Prowizja",
        //             "balanceEngine" => "BITBAY"
        //         ),
        //         "errors" => null
        //     }
        //
        $status = $this->safe_string($transfer, 'status');
        $fromAccount = $this->safe_value($transfer, 'from', array());
        $fromId = $this->safe_string($fromAccount, 'id');
        $to = $this->safe_value($transfer, 'to', array());
        $toId = $this->safe_string($to, 'id');
        $currencyId = $this->safe_string($fromAccount, 'currency');
        return array(
            'info' => $transfer,
            'id' => null,
            'timestamp' => null,
            'datetime' => null,
            'currency' => $this->safe_currency_code($currencyId, $currency),
            'amount' => null,
            'fromAccount' => $fromId,
            'toAccount' => $toId,
            'status' => $this->parse_transfer_status($status),
        );
    }

    public function parse_transfer_status(?string $status): ?string {
        $statuses = array(
            'Ok' => 'ok',
            'Fail' => 'failed',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function withdraw(string $code, float $amount, string $address, ?string $tag = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $address, $tag, $params) {
            /**
             *
             * @see https://docs.zondacrypto.exchange/reference/crypto-withdrawal-1
             *
             * make a withdrawal
             * @param {string} $code unified $currency $code
             * @param {float} $amount the $amount to withdraw
             * @param {string} $address the $address to withdraw to
             * @param {string} $tag
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~
             */
            list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params);
            $this->check_address($address);
            Async\await($this->load_markets());
            $response = null;
            $currency = $this->currency($code);
            $request = array(
                'currency' => $currency['id'],
                'amount' => $amount,
                'address' => $address,
                // $request['balanceId'] = $params['balanceId']; // Wallet id used for withdrawal. If not provided, any BITBAY wallet with sufficient funds is used. If BITBAYPAY wallet should be used parameter must be explicitly specified.
            );
            if ($this->is_fiat($code)) {
                // $request['swift'] = $params['swift']; // Bank identifier, if required.
                $response = Async\await($this->v1_01PrivatePostApiPaymentsWithdrawalsFiat ($this->extend($request, $params)));
            } else {
                if ($tag !== null) {
                    $request['tag'] = $tag;
                }
                $response = Async\await($this->v1_01PrivatePostApiPaymentsWithdrawalsCrypto ($this->extend($request, $params)));
            }
            //
            //     {
            //         "status" => "Ok",
            //         "data" => {
            //           "id" => "65e01087-afb0-4ab2-afdb-cc925e360296"
            //         }
            //     }
            //
            $data = $this->safe_dict($response, 'data');
            return $this->parse_transaction($data, $currency);
        }) ();
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // withdraw
        //
        //     {
        //         "id" => "65e01087-afb0-4ab2-afdb-cc925e360296"
        //     }
        //
        $currency = $this->safe_currency(null, $currency);
        return array(
            'id' => $this->safe_string($transaction, 'id'),
            'txid' => null,
            'timestamp' => null,
            'datetime' => null,
            'network' => null,
            'addressFrom' => null,
            'address' => null,
            'addressTo' => null,
            'amount' => null,
            'type' => null,
            'currency' => $currency['code'],
            'status' => null,
            'updated' => null,
            'tagFrom' => null,
            'tag' => null,
            'tagTo' => null,
            'comment' => null,
            'internal' => null,
            'fee' => null,
            'info' => $transaction,
        );
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $url = $this->implode_hostname($this->urls['api'][$api]);
        if ($api === 'public') {
            $query = $this->omit($params, $this->extract_params($path));
            $url .= '/' . $this->implode_params($path, $params) . '.json';
            if ($query) {
                $url .= '?' . $this->urlencode($query);
            }
        } elseif ($api === 'v1_01Public') {
            $query = $this->omit($params, $this->extract_params($path));
            $url .= '/' . $this->implode_params($path, $params);
            if ($query) {
                $url .= '?' . $this->urlencode($query);
            }
        } elseif ($api === 'v1_01Private') {
            $this->check_required_credentials();
            $query = $this->omit($params, $this->extract_params($path));
            $url .= '/' . $this->implode_params($path, $params);
            $nonce = (string) $this->milliseconds();
            $payload = null;
            if ($method !== 'POST') {
                if ($query) {
                    $url .= '?' . $this->urlencode($query);
                }
                $payload = $this->apiKey . $nonce;
            } elseif ($body === null) {
                $body = $this->json($query);
                $payload = $this->apiKey . $nonce . $body;
            }
            $headers = array(
                'Request-Timestamp' => $nonce,
                'Operation-Id' => $this->uuid(),
                'API-Key' => $this->apiKey,
                'API-Hash' => $this->hmac($this->encode($payload), $this->encode($this->secret), 'sha512'),
                'Content-Type' => 'application/json',
            );
        } else {
            $this->check_required_credentials();
            $body = $this->urlencode($this->extend(array(
                'method' => $path,
                'moment' => $this->nonce(),
            ), $params));
            $headers = array(
                'Content-Type' => 'application/x-www-form-urlencoded',
                'API-Key' => $this->apiKey,
                'API-Hash' => $this->hmac($this->encode($body), $this->encode($this->secret), 'sha512'),
            );
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function handle_errors(int $httpCode, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) {
        if ($response === null) {
            return null; // fallback to default $error handler
        }
        if (is_array($response) && array_key_exists('code', $response)) {
            //
            // bitbay returns the integer "success" => 1 key from their private API
            // or an integer "code" value from 0 to 510 and an $error message
            //
            //      array( "success" => 1, ... )
            //      array( 'code' => 502, "message" => "Invalid sign" )
            //      array( 'code' => 0, "message" => "offer funds not exceeding minimums" )
            //
            //      400 At least one parameter wasn't set
            //      401 Invalid order type
            //      402 No orders with specified currencies
            //      403 Invalid payment currency name
            //      404 Error. Wrong transaction type
            //      405 Order with this id doesn't exist
            //      406 No enough money or crypto
            //      408 Invalid currency name
            //      501 Invalid public key
            //      502 Invalid sign
            //      503 Invalid moment parameter. Request time doesn't match current server time
            //      504 Invalid $method
            //      505 Key has no permission for this action
            //      506 Account locked. Please contact with customer service
            //      509 The BIC/SWIFT is required for this currency
            //      510 Invalid market name
            //
            $code = $this->safe_string($response, 'code'); // always an integer
            $feedback = $this->id . ' ' . $body;
            $this->throw_exactly_matched_exception($this->exceptions, $code, $feedback);
            throw new ExchangeError($feedback);
        } elseif (is_array($response) && array_key_exists('status', $response)) {
            //
            //      array("status":"Fail","errors":["OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS"])
            //
            $status = $this->safe_string($response, 'status');
            if ($status === 'Fail') {
                $errors = $this->safe_value($response, 'errors');
                $feedback = $this->id . ' ' . $body;
                for ($i = 0; $i < count($errors); $i++) {
                    $error = $errors[$i];
                    $this->throw_exactly_matched_exception($this->exceptions, $error, $feedback);
                }
                throw new ExchangeError($feedback);
            }
        }
        return null;
    }
}
