<?php

namespace ccxt;

// 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\abstract\bigone as Exchange;

class bigone extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'bigone',
            'name' => 'BigONE',
            'countries' => array( 'CN' ),
            'version' => 'v3',
            'rateLimit' => 20, // 500 requests per 10 seconds
            'has' => array(
                'CORS' => null,
                'spot' => true,
                'margin' => false,
                'swap' => true,
                'future' => null, // has but unimplemented
                'option' => false,
                'cancelAllOrders' => true,
                'cancelOrder' => true,
                'createMarketBuyOrderWithCost' => true,
                'createMarketOrderWithCost' => false,
                'createMarketSellOrderWithCost' => false,
                'createOrder' => true,
                'createPostOnlyOrder' => true,
                'createStopLimitOrder' => true,
                'createStopMarketOrder' => true,
                'createStopOrder' => true,
                'fetchBalance' => true,
                'fetchClosedOrders' => true,
                'fetchCurrencies' => true,
                'fetchDepositAddress' => true,
                'fetchDepositAddresses' => false,
                'fetchDepositAddressesByNetwork' => false,
                'fetchDeposits' => true,
                'fetchFundingHistory' => false,
                'fetchFundingRate' => false,
                'fetchFundingRateHistory' => false,
                'fetchFundingRates' => false,
                'fetchMarkets' => true,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenOrders' => true,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrders' => true,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => false,
                'fetchTradingFees' => false,
                'fetchTransactionFees' => false,
                'fetchWithdrawals' => true,
                'transfer' => true,
                'withdraw' => true,
            ),
            'timeframes' => array(
                '1m' => 'min1',
                '5m' => 'min5',
                '15m' => 'min15',
                '30m' => 'min30',
                '1h' => 'hour1',
                '3h' => 'hour3',
                '4h' => 'hour4',
                '6h' => 'hour6',
                '12h' => 'hour12',
                '1d' => 'day1',
                '1w' => 'week1',
                '1M' => 'month1',
            ),
            'hostname' => 'big.one', // or 'bigone.com'
            'urls' => array(
                'logo' => 'https://github.com/user-attachments/assets/4e5cfd53-98cc-4b90-92cd-0d7b512653d1',
                'api' => array(
                    'public' => 'https://{hostname}/api/v3',
                    'private' => 'https://{hostname}/api/v3/viewer',
                    'contractPublic' => 'https://{hostname}/api/contract/v2',
                    'contractPrivate' => 'https://{hostname}/api/contract/v2',
                    'webExchange' => 'https://{hostname}/api/',
                ),
                'www' => 'https://big.one',
                'doc' => 'https://open.big.one/docs/api.html',
                'fees' => 'https://bigone.zendesk.com/hc/en-us/articles/115001933374-BigONE-Fee-Policy',
                'referral' => 'https://b1.run/users/new?code=D3LLBVFT',
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        'ping',
                        'asset_pairs',
                        'asset_pairs/{asset_pair_name}/depth',
                        'asset_pairs/{asset_pair_name}/trades',
                        'asset_pairs/{asset_pair_name}/ticker',
                        'asset_pairs/{asset_pair_name}/candles',
                        'asset_pairs/tickers',
                    ),
                ),
                'private' => array(
                    'get' => array(
                        'accounts',
                        'fund/accounts',
                        'assets/{asset_symbol}/address',
                        'orders',
                        'orders/{id}',
                        'orders/multi',
                        'trades',
                        'withdrawals',
                        'deposits',
                    ),
                    'post' => array(
                        'orders',
                        'orders/{id}/cancel',
                        'orders/cancel',
                        'withdrawals',
                        'transfer',
                    ),
                ),
                'contractPublic' => array(
                    'get' => array(
                        'symbols',
                        'instruments',
                        'depth@{symbol}/snapshot',
                        'instruments/difference',
                        'instruments/prices',
                    ),
                ),
                'contractPrivate' => array(
                    'get' => array(
                        'accounts',
                        'orders/{id}',
                        'orders',
                        'orders/opening',
                        'orders/count',
                        'orders/opening/count',
                        'trades',
                        'trades/count',
                    ),
                    'post' => array(
                        'orders',
                        'orders/batch',
                    ),
                    'put' => array(
                        'positions/{symbol}/margin',
                        'positions/{symbol}/risk-limit',
                    ),
                    'delete' => array(
                        'orders/{id}',
                        'orders/batch',
                    ),
                ),
                'webExchange' => array(
                    'get' => array(
                        'v3/assets',
                    ),
                ),
            ),
            'fees' => array(
                'trading' => array(
                    'maker' => $this->parse_number('0.001'),
                    'taker' => $this->parse_number('0.001'),
                ),
                'funding' => array(
                    'withdraw' => array(),
                ),
            ),
            'options' => array(
                'createMarketBuyOrderRequiresPrice' => true,
                'accountsByType' => array(
                    'spot' => 'SPOT',
                    'fund' => 'FUND',
                    'funding' => 'FUND',
                    'future' => 'CONTRACT',
                    'swap' => 'CONTRACT',
                ),
                'transfer' => array(
                    'fillResponseFromRequest' => true,
                ),
                'exchangeMillisecondsCorrection' => -100,
                'fetchCurrencies' => array(
                    'webApiEnable' => true, // fetches from WEB
                    'webApiRetries' => 5,
                    'webApiMuteFailure' => true,
                ),
                'defaultNetwork' => 'ERC20',
                'defaultNetworks' => array(
                    'USDT' => 'TRC20',
                ),
                'networks' => array(
                    'ABBC' => 'ABBC',
                    'ACA' => 'Acala',
                    'AE' => 'Aeternity',
                    'ALGO' => 'Algorand',
                    'APT' => 'Aptos',
                    'AR' => 'Arweave',
                    'ASTR' => 'Astar',
                    'AVAXC' => 'Avax',
                    'AVAXX' => 'AvaxChain',
                    'BEAM' => 'Beam',
                    'BEP20' => 'BinanceSmartChain',
                    'BITCI' => 'BitciChain',
                    'BTC' => 'Bitcoin',
                    'BCH' => 'BitcoinCash',
                    'BSV' => 'BitcoinSV',
                    'CELO' => 'Celo',
                    'CKKB' => 'CKB',
                    'ATOM' => 'Cosmos',
                    'CRC20' => 'CRO',
                    'DASH' => 'Dash',
                    'DOGE' => 'Dogecoin',
                    'XEC' => 'ECash',
                    'EOS' => 'EOS',
                    'ETH' => 'Ethereum',
                    'ETC' => 'EthereumClassic',
                    'ETHW' => 'EthereumPow',
                    'FTM' => 'Fantom',
                    'FIL' => 'Filecoin',
                    'FSN' => 'Fusion',
                    'GRIN' => 'Grin',
                    'ONE' => 'Harmony',
                    'HRC20' => 'Hecochain',
                    'HBAR' => 'Hedera',
                    'HNT' => 'Helium',
                    'ZEN' => 'Horizen',
                    'IOST' => 'IOST',
                    'IRIS' => 'IRIS',
                    'KLAY' => 'Klaytn',
                    'KSM' => 'Kusama',
                    'LTC' => 'Litecoin',
                    'XMR' => 'Monero',
                    'GLMR' => 'Moonbeam',
                    'NEAR' => 'Near',
                    'NEO' => 'Neo',
                    'NEON3' => 'NeoN3',
                    'OASIS' => 'Oasis',
                    'OKC' => 'Okexchain',
                    'ONT' => 'Ontology',
                    'OPTIMISM' => 'Optimism',
                    'DOT' => 'Polkadot',
                    'MATIC' => 'Polygon',
                    'QTUM' => 'Qtum',
                    'REI' => 'REI',
                    'XRP' => 'Ripple',
                    'SGB' => 'SGB',
                    'SDN' => 'Shiden',
                    'SOL' => 'Solana',
                    'XLM' => 'Stellar',
                    'TERA' => 'Tera',
                    'XTZ' => 'Tezos',
                    'TRC20' => 'Tron',
                    'VET' => 'Vechain',
                    'VSYS' => 'VSystems',
                    'WAX' => 'WAX',
                    'ZEC' => 'Zcash',
                    // todo => uncomment after consensus
                    // 'BITSHARES_OLD' => 'Bitshares',
                    // 'BITSHARES_NEW' => 'NewBitshares',
                    // 'MOBILECOIN' => 'Mobilecoin',
                    // 'LBRY' => 'Lbry',
                    // 'ZEEPIN' => 'Zeepin',
                    // 'WAYFCOIN' => 'Wayfcoin',
                    // 'UCACOIN' => 'Ucacoin',
                    // 'VANILLACASH' => 'Vcash',
                    // 'LAMDEN' => 'Lamden',
                    // 'GXSHARES' => 'Gxshares',
                    // 'ICP' => 'Dfinity',
                    // 'CLOVER' => 'Clover',
                    // 'CLASSZZ' => 'Classzz',
                    // 'CLASSZZ_V2' => 'ClasszzV2',
                    // 'CHAINX_V2' => 'ChainxV2',
                    // 'BITCOINDIAMON' => 'BitcoinDiamond',
                    // 'BITCOINGOLD' => 'BitcoinGold',
                    // 'BUTTRUSTSYSTEM' => 'BitTrustSystem',
                    // 'BYTOM_V2' => 'BytomV2',
                    // 'LIBONOMY' => 'Libonomy',
                    // 'TERRACLASSIC' => 'Terra',
                    // 'TERRA' => 'Terra2',
                    // 'SUPERBITCOIN' => 'SuperBitcoin',
                    // 'SIACLASSIC' => 'Sia',
                    // 'SIACOIN' => 'SiaCore',
                    // 'PARALLELFINANCE' => 'Parallel',
                    // 'PLCULTIMA' => 'Plcu',
                    // 'PLCULTIMA2' => 'Plcu2',
                    // undetermined => XinFin, YAS, Ycash
                ),
            ),
            'features' => array(
                'default' => array(
                    'sandbox' => false,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => true,
                        'triggerPriceType' => null,
                        'triggerDirection' => true, // todo implement
                        'stopLossPrice' => false, // todo by trigger
                        'takeProfitPrice' => false, // todo by trigger
                        'attachedStopLossTakeProfit' => null,
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => false,
                            'PO' => true,
                            'GTD' => false,
                        ),
                        'hedged' => false,
                        'trailing' => false,
                        'leverage' => false,
                        'marketBuyRequiresPrice' => true,
                        'marketBuyByCost' => true,
                        'selfTradePrevention' => false,
                        'iceberg' => false,
                    ),
                    'createOrders' => null, // todo => implement
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => 200,
                        'daysBack' => null,
                        'untilDays' => null,
                        'symbolRequired' => true,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                        'limit' => 200,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOrders' => array(
                        'marginMode' => false,
                        'limit' => 200,
                        'daysBack' => null,
                        'untilDays' => null,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchClosedOrders' => array(
                        'marginMode' => false,
                        'limit' => 200,
                        'daysBack' => null,
                        'daysBackCanceled' => null,
                        'untilDays' => null,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 500,
                    ),
                ),
                'spot' => array(
                    'extends' => 'default',
                ),
                'forDerivatives' => array(
                    'extends' => 'default',
                    'createOrder' => array(
                        // todo => implement
                        'triggerPriceType' => array(
                            'mark' => true,
                            'index' => true,
                            'last' => true,
                        ),
                    ),
                    'fetchOrders' => array(
                        'daysBack' => 100000,
                        'untilDays' => 100000,
                    ),
                    'fetchClosedOrders' => array(
                        'daysBack' => 100000,
                        'untilDays' => 100000,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'forDerivatives',
                    ),
                    'inverse' => array(
                        'extends' => 'forDerivatives',
                    ),
                ),
                'future' => array(
                    'linear' => null,
                    'inverse' => null,
                ),
            ),
            'precisionMode' => TICK_SIZE,
            'exceptions' => array(
                'exact' => array(
                    '10001' => '\\ccxt\\BadRequest', // syntax error
                    '10005' => '\\ccxt\\ExchangeError', // internal error
                    "Amount's scale must greater than AssetPair's base scale" => '\\ccxt\\InvalidOrder',
                    "Price mulit with amount should larger than AssetPair's min_quote_value" => '\\ccxt\\InvalidOrder',
                    '10007' => '\\ccxt\\BadRequest', // parameter error, array("code":10007,"message":"Amount's scale must greater than AssetPair's base scale")
                    '10011' => '\\ccxt\\ExchangeError', // system error
                    '10013' => '\\ccxt\\BadSymbol', // array("code":10013,"message":"Resource not found")
                    '10014' => '\\ccxt\\InsufficientFunds', // array("code":10014,"message":"Insufficient funds")
                    '10403' => '\\ccxt\\PermissionDenied', // permission denied
                    '10429' => '\\ccxt\\RateLimitExceeded', // too many requests
                    '40004' => '\\ccxt\\AuthenticationError', // array("code":40004,"message":"invalid jwt")
                    '40103' => '\\ccxt\\AuthenticationError', // invalid otp code
                    '40104' => '\\ccxt\\AuthenticationError', // invalid asset pin code
                    '40301' => '\\ccxt\\PermissionDenied', // array("code":40301,"message":"Permission denied withdrawal create")
                    '40302' => '\\ccxt\\ExchangeError', // already requested
                    '40601' => '\\ccxt\\ExchangeError', // resource is locked
                    '40602' => '\\ccxt\\ExchangeError', // resource is depleted
                    '40603' => '\\ccxt\\InsufficientFunds', // insufficient resource
                    '40604' => '\\ccxt\\InvalidOrder', // array("code":40604,"message":"Price exceed the maximum order price")
                    '40605' => '\\ccxt\\InvalidOrder', // array("code":40605,"message":"Price less than the minimum order price")
                    '40120' => '\\ccxt\\InvalidOrder', // Order is in trading
                    '40121' => '\\ccxt\\InvalidOrder', // Order is already cancelled or filled
                    '60100' => '\\ccxt\\BadSymbol', // array("code":60100,"message":"Asset pair is suspended")
                ),
                'broad' => array(
                ),
            ),
            'commonCurrencies' => array(
                'CRE' => 'Cybereits',
                'FXT' => 'FXTTOKEN',
                'FREE' => 'FreeRossDAO',
                'MBN' => 'Mobilian Coin',
                'ONE' => 'BigONE Token',
            ),
        ));
    }

    public function fetch_currencies($params = array ()): ?array {
        /**
         * fetches all available currencies on an exchange
         * @param {dict} [$params] extra parameters specific to the exchange API endpoint
         * @return {dict} an associative dictionary of currencies
         */
        // we use undocumented link (possible, less informative alternative is : https://big.one/api/uc/v3/assets/accounts)
        $data = $this->fetch_web_endpoint('fetchCurrencies', 'webExchangeGetV3Assets', true);
        if ($data === null) {
            return array();
        }
        //
        // {
        //     "code" => "0",
        //     "message" => "",
        //     "data" => array(
        //       {
        //             "uuid" => "17082d1c-0195-4fb6-8779-2cdbcb9eeb3c",
        //             "symbol" => "USDT",
        //             "name" => "TetherUS",
        //             "scale" => 12,
        //             "is_fiat" => false,
        //             "is_transfer_enabled" => true,
        //             "transfer_scale" => 12,
        //             "binding_gateways" => array(
        //                 array(
        //                     "guid" => "07efc37f-d1ec-4bc9-8339-a745256ea2ba",
        //                     "is_deposit_enabled" => true,
        //                     "gateway_name" => "Ethereum",
        //                     "min_withdrawal_amount" => "0.000001",
        //                     "withdrawal_fee" => "5.71",
        //                     "is_withdrawal_enabled" => true,
        //                     "min_deposit_amount" => "0.000001",
        //                     "is_memo_required" => false,
        //                     "withdrawal_scale" => 6,
        //                     "scale" => 12
        //                 ),
        //                 array(
        //                     "guid" => "4e387a9a-a480-40a3-b4ae-ed1773c2db5a",
        //                     "is_deposit_enabled" => true,
        //                     "gateway_name" => "BinanceSmartChain",
        //                     "min_withdrawal_amount" => "10",
        //                     "withdrawal_fee" => "5",
        //                     "is_withdrawal_enabled" => false,
        //                     "min_deposit_amount" => "1",
        //                     "is_memo_required" => false,
        //                     "withdrawal_scale" => 8,
        //                     "scale" => 12
        //                 }
        //             )
        //         ),
        //       ...
        //     ),
        // }
        //
        $currenciesData = $this->safe_list($data, 'data', array());
        $result = array();
        for ($i = 0; $i < count($currenciesData); $i++) {
            $currency = $currenciesData[$i];
            $id = $this->safe_string($currency, 'symbol');
            $code = $this->safe_currency_code($id);
            $name = $this->safe_string($currency, 'name');
            $networks = array();
            $chains = $this->safe_list($currency, 'binding_gateways', array());
            $currencyMaxPrecision = $this->parse_precision($this->safe_string_2($currency, 'withdrawal_scale', 'scale'));
            for ($j = 0; $j < count($chains); $j++) {
                $chain = $chains[$j];
                $networkId = $this->safe_string($chain, 'gateway_name');
                $networkCode = $this->network_id_to_code($networkId);
                $deposit = $this->safe_bool($chain, 'is_deposit_enabled');
                $withdraw = $this->safe_bool($chain, 'is_withdrawal_enabled');
                $minDepositAmount = $this->safe_string($chain, 'min_deposit_amount');
                $minWithdrawalAmount = $this->safe_string($chain, 'min_withdrawal_amount');
                $withdrawalFee = $this->safe_string($chain, 'withdrawal_fee');
                $precision = $this->parse_precision($this->safe_string_2($chain, 'withdrawal_scale', 'scale'));
                $networks[$networkCode] = array(
                    'id' => $networkId,
                    'network' => $networkCode,
                    'margin' => null,
                    'deposit' => $deposit,
                    'withdraw' => $withdraw,
                    'active' => null,
                    'fee' => $this->parse_number($withdrawalFee),
                    'precision' => $this->parse_number($precision),
                    'limits' => array(
                        'deposit' => array(
                            'min' => $minDepositAmount,
                            'max' => null,
                        ),
                        'withdraw' => array(
                            'min' => $minWithdrawalAmount,
                            'max' => null,
                        ),
                    ),
                    'info' => $chain,
                );
            }
            $chainLength = count($chains);
            $type = null;
            if ($this->safe_bool($currency, 'is_fiat')) {
                $type = 'fiat';
            } elseif ($chainLength === 0) {
                if ($this->is_leveraged_currency($id)) {
                    $type = 'leveraged';
                } else {
                    $type = 'other';
                }
            } else {
                $type = 'crypto';
            }
            $result[$code] = $this->safe_currency_structure(array(
                'id' => $id,
                'code' => $code,
                'info' => $currency,
                'name' => $name,
                'type' => $type,
                'active' => null,
                'deposit' => null,
                'withdraw' => null,
                'fee' => null,
                'precision' => $this->parse_number($currencyMaxPrecision),
                'limits' => array(
                    'amount' => array(
                        'min' => null,
                        'max' => null,
                    ),
                    'withdraw' => array(
                        'min' => null,
                        'max' => null,
                    ),
                ),
                'networks' => $networks,
            ));
        }
        return $result;
    }

    public function fetch_markets($params = array ()): array {
        /**
         * retrieves data on all $markets for bigone
         *
         * @see https://open.big.one/docs/spot_asset_pair.html
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} an array of objects representing $market data
         */
        $promises = array( $this->publicGetAssetPairs ($params), $this->contractPublicGetSymbols ($params) );
        $promisesResult = $promises;
        $response = $promisesResult[0];
        $contractResponse = $promisesResult[1];
        //
        //     {
        //         "code":0,
        //         "data":array(
        //             array(
        //                 "id":"01e48809-b42f-4a38-96b1-c4c547365db1",
        //                 "name":"PCX-BTC",
        //                 "quote_scale":7,
        //                 "quote_asset":array(
        //                     "id":"0df9c3c3-255a-46d7-ab82-dedae169fba9",
        //                     "symbol":"BTC",
        //                     "name":"Bitcoin",
        //                 ),
        //                 "base_asset":array(
        //                     "id":"405484f7-4b03-4378-a9c1-2bd718ecab51",
        //                     "symbol":"PCX",
        //                     "name":"ChainX",
        //                 ),
        //                 "base_scale":3,
        //                 "min_quote_value":"0.0001",
        //                 "max_quote_value":"35"
        //             ),
        //         )
        //     }
        //
        //
        //    array(
        //        array(
        //            "baseCurrency" => "BTC",
        //            "multiplier" => 1,
        //            "enable" => true,
        //            "priceStep" => 0.5,
        //            "maxRiskLimit" => 1000,
        //            "pricePrecision" => 1,
        //            "maintenanceMargin" => 0.00500,
        //            "symbol" => "BTCUSD",
        //            "valuePrecision" => 4,
        //            "minRiskLimit" => 100,
        //            "riskLimit" => 100,
        //            "isInverse" => true,
        //            "riskStep" => 1,
        //            "settleCurrency" => "BTC",
        //            "baseName" => "Bitcoin",
        //            "feePrecision" => 8,
        //            "priceMin" => 0.5,
        //            "priceMax" => 1E+6,
        //            "initialMargin" => 0.01000,
        //            "quoteCurrency" => "USD"
        //        ),
        //        ...
        //    )
        //
        $markets = $this->safe_list($response, 'data', array());
        $result = array();
        for ($i = 0; $i < count($markets); $i++) {
            $market = $markets[$i];
            $baseAsset = $this->safe_dict($market, 'base_asset', array());
            $quoteAsset = $this->safe_dict($market, 'quote_asset', array());
            $baseId = $this->safe_string($baseAsset, 'symbol');
            $quoteId = $this->safe_string($quoteAsset, 'symbol');
            $base = $this->safe_currency_code($baseId);
            $quote = $this->safe_currency_code($quoteId);
            $result[] = $this->safe_market_structure(array(
                'id' => $this->safe_string($market, 'name'),
                'uuid' => $this->safe_string($market, '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' => true,
                'contract' => false,
                'linear' => null,
                'inverse' => null,
                'contractSize' => null,
                'expiry' => null,
                'expiryDatetime' => null,
                'strike' => null,
                'optionType' => null,
                'precision' => array(
                    'amount' => $this->parse_number($this->parse_precision($this->safe_string($market, 'base_scale'))),
                    'price' => $this->parse_number($this->parse_precision($this->safe_string($market, 'quote_scale'))),
                ),
                'limits' => array(
                    'leverage' => array(
                        'min' => null,
                        'max' => null,
                    ),
                    'amount' => array(
                        'min' => null,
                        'max' => null,
                    ),
                    'price' => array(
                        'min' => null,
                        'max' => null,
                    ),
                    'cost' => array(
                        'min' => $this->safe_number($market, 'min_quote_value'),
                        'max' => $this->safe_number($market, 'max_quote_value'),
                    ),
                ),
                'created' => null,
                'info' => $market,
            ));
        }
        for ($i = 0; $i < count($contractResponse); $i++) {
            $market = $contractResponse[$i];
            $baseId = $this->safe_string($market, 'baseCurrency');
            $quoteId = $this->safe_string($market, 'quoteCurrency');
            $settleId = $this->safe_string($market, 'settleCurrency');
            $marketId = $this->safe_string($market, 'symbol');
            $base = $this->safe_currency_code($baseId);
            $quote = $this->safe_currency_code($quoteId);
            $settle = $this->safe_currency_code($settleId);
            $inverse = $this->safe_bool($market, 'isInverse');
            $result[] = $this->safe_market_structure(array(
                'id' => $marketId,
                'symbol' => $base . '/' . $quote . ':' . $settle,
                'base' => $base,
                'quote' => $quote,
                'settle' => $settle,
                'baseId' => $baseId,
                'quoteId' => $quoteId,
                'settleId' => $settleId,
                'type' => 'swap',
                'spot' => false,
                'margin' => false,
                'swap' => true,
                'future' => false,
                'option' => false,
                'active' => $this->safe_bool($market, 'enable'),
                'contract' => true,
                'linear' => !$inverse,
                'inverse' => $inverse,
                'contractSize' => $this->safe_number($market, 'multiplier'),
                'expiry' => null,
                'expiryDatetime' => null,
                'strike' => null,
                'optionType' => null,
                'precision' => array(
                    'amount' => $this->parse_number($this->parse_precision($this->safe_string($market, 'valuePrecision'))),
                    'price' => $this->parse_number($this->parse_precision($this->safe_string($market, 'pricePrecision'))),
                ),
                'limits' => array(
                    'leverage' => array(
                        'min' => null,
                        'max' => null,
                    ),
                    'amount' => array(
                        'min' => null,
                        'max' => null,
                    ),
                    'price' => array(
                        'min' => $this->safe_number($market, 'priceMin'),
                        'max' => $this->safe_number($market, 'priceMax'),
                    ),
                    'cost' => array(
                        'min' => $this->safe_number($market, 'initialMargin'),
                        'max' => null,
                    ),
                ),
                'info' => $market,
            ));
        }
        return $result;
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        //
        // spot
        //
        //    {
        //        "asset_pair_name" => "ETH-BTC",
        //        "bid" => array(
        //            "price" => "0.021593",
        //            "order_count" => 1,
        //            "quantity" => "0.20936"
        //        ),
        //        "ask" => array(
        //            "price" => "0.021613",
        //            "order_count" => 1,
        //            "quantity" => "2.87064"
        //        ),
        //        "open" => "0.021795",
        //        "high" => "0.021795",
        //        "low" => "0.021471",
        //        "close" => "0.021613",
        //        "volume" => "117078.90431",
        //        "daily_change" => "-0.000182"
        //    }
        //
        // contract
        //
        //    {
        //        "usdtPrice" => 1.00031998,
        //        "symbol" => "BTCUSD",
        //        "btcPrice" => 34700.4,
        //        "ethPrice" => 1787.83,
        //        "nextFundingRate" => 0.00010,
        //        "fundingRate" => 0.00010,
        //        "latestPrice" => 34708.5,
        //        "last24hPriceChange" => 0.0321,
        //        "indexPrice" => 34700.4,
        //        "volume24h" => 261319063,
        //        "turnover24h" => 8204.129380685496,
        //        "nextFundingTime" => 1698285600000,
        //        "markPrice" => 34702.4646738,
        //        "last24hMaxPrice" => 35127.5,
        //        "volume24hInUsd" => 0.0,
        //        "openValue" => 32.88054722085945,
        //        "last24hMinPrice" => 33552.0,
        //        "openInterest" => 1141372.0
        //    }
        //
        $marketType = (is_array($ticker) && array_key_exists('asset_pair_name', $ticker)) ? 'spot' : 'swap';
        $marketId = $this->safe_string_2($ticker, 'asset_pair_name', 'symbol');
        $symbol = $this->safe_symbol($marketId, $market, '-', $marketType);
        $close = $this->safe_string_2($ticker, 'close', 'latestPrice');
        $bid = $this->safe_dict($ticker, 'bid', array());
        $ask = $this->safe_dict($ticker, 'ask', array());
        return $this->safe_ticker(array(
            'symbol' => $symbol,
            'timestamp' => null,
            'datetime' => null,
            'high' => $this->safe_string_2($ticker, 'high', 'last24hMaxPrice'),
            'low' => $this->safe_string_2($ticker, 'low', 'last24hMinPrice'),
            'bid' => $this->safe_string($bid, 'price'),
            'bidVolume' => $this->safe_string($bid, 'quantity'),
            'ask' => $this->safe_string($ask, 'price'),
            'askVolume' => $this->safe_string($ask, 'quantity'),
            'vwap' => null,
            'open' => $this->safe_string($ticker, 'open'),
            'close' => $close,
            'last' => $close,
            'previousClose' => null,
            'change' => $this->safe_string_2($ticker, 'daily_change', 'last24hPriceChange'),
            'percentage' => null,
            'average' => null,
            'baseVolume' => $this->safe_string_2($ticker, 'volume', 'volume24h'),
            'quoteVolume' => $this->safe_string($ticker, 'volume24hInUsd'),
            'markPrice' => $this->safe_string($ticker, 'markPrice'),
            'indexPrice' => $this->safe_string($ticker, 'indexPrice'),
            'info' => $ticker,
        ), $market);
    }

    public function fetch_ticker(string $symbol, $params = array ()): array {
        /**
         * fetches a price $ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market
         *
         * @see https://open.big.one/docs/spot_tickers.html
         *
         * @param {string} $symbol unified $symbol of the $market to fetch the $ticker for
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=$ticker-structure $ticker structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $type = null;
        list($type, $params) = $this->handle_market_type_and_params('fetchTicker', $market, $params);
        if ($type === 'spot') {
            $request = array(
                'asset_pair_name' => $market['id'],
            );
            $response = $this->publicGetAssetPairsAssetPairNameTicker ($this->extend($request, $params));
            //
            //     {
            //         "code":0,
            //         "data":{
            //             "asset_pair_name":"ETH-BTC",
            //             "bid":array("price":"0.021593","order_count":1,"quantity":"0.20936"),
            //             "ask":array("price":"0.021613","order_count":1,"quantity":"2.87064"),
            //             "open":"0.021795",
            //             "high":"0.021795",
            //             "low":"0.021471",
            //             "close":"0.021613",
            //             "volume":"117078.90431",
            //             "daily_change":"-0.000182"
            //         }
            //     }
            //
            $ticker = $this->safe_dict($response, 'data', array());
            return $this->parse_ticker($ticker, $market);
        } else {
            $tickers = $this->fetch_tickers(array( $symbol ), $params);
            return $this->safe_value($tickers, $symbol);
        }
    }

    public function fetch_tickers(?array $symbols = null, $params = array ()): array {
        /**
         * fetches price $tickers for multiple markets, statistical information calculated over the past 24 hours for each $market
         *
         * @see https://open.big.one/docs/spot_tickers.html
         *
         * @param {string[]} [$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
         * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
         */
        $this->load_markets();
        $market = null;
        $symbol = $this->safe_string($symbols, 0);
        if ($symbol !== null) {
            $market = $this->market($symbol);
        }
        $type = null;
        list($type, $params) = $this->handle_market_type_and_params('fetchTickers', $market, $params);
        $isSpot = $type === 'spot';
        $request = array();
        $symbols = $this->market_symbols($symbols);
        $data = null;
        if ($isSpot) {
            if ($symbols !== null) {
                $ids = $this->market_ids($symbols);
                $request['pair_names'] = implode(',', $ids);
            }
            $response = $this->publicGetAssetPairsTickers ($this->extend($request, $params));
            //
            //    {
            //        "code" => 0,
            //        "data" => array(
            //            array(
            //                "asset_pair_name" => "PCX-BTC",
            //                "bid" => array(
            //                    "price" => "0.000234",
            //                    "order_count" => 1,
            //                    "quantity" => "0.518"
            //                ),
            //                "ask" => array(
            //                    "price" => "0.0002348",
            //                    "order_count" => 1,
            //                    "quantity" => "2.348"
            //                ),
            //                "open" => "0.0002343",
            //                "high" => "0.0002348",
            //                "low" => "0.0002162",
            //                "close" => "0.0002348",
            //                "volume" => "12887.016",
            //                "daily_change" => "0.0000005"
            //            ),
            //            ...
            //        )
            //    }
            //
            $data = $this->safe_list($response, 'data', array());
        } else {
            $data = $this->contractPublicGetInstruments ($params);
            //
            //    array(
            //        {
            //            "usdtPrice" => 1.00031998,
            //            "symbol" => "BTCUSD",
            //            "btcPrice" => 34700.4,
            //            "ethPrice" => 1787.83,
            //            "nextFundingRate" => 0.00010,
            //            "fundingRate" => 0.00010,
            //            "latestPrice" => 34708.5,
            //            "last24hPriceChange" => 0.0321,
            //            "indexPrice" => 34700.4,
            //            "volume24h" => 261319063,
            //            "turnover24h" => 8204.129380685496,
            //            "nextFundingTime" => 1698285600000,
            //            "markPrice" => 34702.4646738,
            //            "last24hMaxPrice" => 35127.5,
            //            "volume24hInUsd" => 0.0,
            //            "openValue" => 32.88054722085945,
            //            "last24hMinPrice" => 33552.0,
            //            "openInterest" => 1141372.0
            //        }
            //        ...
            //    )
            //
        }
        $tickers = $this->parse_tickers($data, $symbols);
        return $this->filter_by_array_tickers($tickers, 'symbol', $symbols);
    }

    public function fetch_time($params = array ()): ?int {
        /**
         * fetches the current integer $timestamp in milliseconds from the exchange server
         *
         * @see https://open.big.one/docs/spot_ping.html
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {int} the current integer $timestamp in milliseconds from the exchange server
         */
        $response = $this->publicGetPing ($params);
        //
        //     {
        //         "data" => {
        //             "timestamp" => 1527665262168391000
        //         }
        //     }
        //
        $data = $this->safe_dict($response, 'data', array());
        $timestamp = $this->safe_integer($data, 'Timestamp');
        return $this->parse_to_int($timestamp / 1000000);
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
         *
         * @see https://open.big.one/docs/contract_misc.html#get-$orderbook-snapshot
         *
         * @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
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $response = null;
        if ($market['contract']) {
            $request = array(
                'symbol' => $market['id'],
            );
            $response = $this->contractPublicGetDepthSymbolSnapshot ($this->extend($request, $params));
            //
            //    {
            //        bids => array(
            //            '20000' => '20',
            //            ...
            //            '34552' => '64851',
            //            '34526.5' => '59594',
            //            ...
            //            '34551.5' => '29711'
            //        ),
            //        asks => array(
            //            '34557' => '34395',
            //            ...
            //            '40000' => '20',
            //            '34611.5' => '56024',
            //            ...
            //            '34578.5' => '66367'
            //        ),
            //        to => '59737174',
            //        lastPrice => '34554.5',
            //        bestPrices => array(
            //            ask => '34557.0',
            //            bid => '34552.0'
            //        ),
            //        from => '0'
            //    }
            //
            return $this->parse_contract_order_book($response, $market['symbol'], $limit);
        } else {
            $request = array(
                'asset_pair_name' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit; // default 50, max 200
            }
            $response = $this->publicGetAssetPairsAssetPairNameDepth ($this->extend($request, $params));
            //
            //     {
            //         "code":0,
            //         "data" => {
            //             "asset_pair_name" => "EOS-BTC",
            //             "bids" => array(
            //                 array( "price" => "42", "order_count" => 4, "quantity" => "23.33363711" )
            //             ),
            //             "asks" => array(
            //                 array( "price" => "45", "order_count" => 2, "quantity" => "4193.3283464" )
            //             )
            //         }
            //     }
            //
            $orderbook = $this->safe_dict($response, 'data', array());
            return $this->parse_order_book($orderbook, $market['symbol'], null, 'bids', 'asks', 'price', 'quantity');
        }
    }

    public function parse_contract_bids_asks($bidsAsks) {
        $bidsAsksKeys = is_array($bidsAsks) ? array_keys($bidsAsks) : array();
        $result = array();
        for ($i = 0; $i < count($bidsAsksKeys); $i++) {
            $price = $bidsAsksKeys[$i];
            $amount = $bidsAsks[$price];
            $result[] = array( $this->parse_number($price), $this->parse_number($amount) );
        }
        return $result;
    }

    public function parse_contract_order_book(array $orderbook, string $symbol, ?int $limit = null): array {
        $responseBids = $this->safe_value($orderbook, 'bids');
        $responseAsks = $this->safe_value($orderbook, 'asks');
        $bids = $this->parse_contract_bids_asks($responseBids);
        $asks = $this->parse_contract_bids_asks($responseAsks);
        return array(
            'symbol' => $symbol,
            'bids' => $this->filter_by_limit($this->sort_by($bids, 0, true), $limit),
            'asks' => $this->filter_by_limit($this->sort_by($asks, 0), $limit),
            'timestamp' => null,
            'datetime' => null,
            'nonce' => null,
        );
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        //
        // fetchTrades (public)
        //
        //     {
        //         "id" => 38199941,
        //         "price" => "3378.67",
        //         "amount" => "0.019812",
        //         "taker_side" => "ASK",
        //         "created_at" => "2019-01-29T06:05:56Z"
        //     }
        //
        // fetchMyTrades (private)
        //
        //     array(
        //         "id" => 10854280,
        //         "asset_pair_name" => "XIN-USDT",
        //         "price" => "70",
        //         "amount" => "1",
        //         "taker_side" => "ASK",
        //         "maker_order_id" => 58284908,
        //         "taker_order_id" => 58284909,
        //         "maker_fee" => "0.0008",
        //         "taker_fee" => "0.07",
        //         "side" => "SELF_TRADING",
        //         "inserted_at" => "2019-04-16T12:00:01Z"
        //     ),
        //
        //     {
        //         "id" => 10854263,
        //         "asset_pair_name" => "XIN-USDT",
        //         "price" => "75.7",
        //         "amount" => "12.743149",
        //         "taker_side" => "BID",
        //         "maker_order_id" => null,
        //         "taker_order_id" => 58284888,
        //         "maker_fee" => null,
        //         "taker_fee" => "0.0025486298",
        //         "side" => "BID",
        //         "inserted_at" => "2019-04-15T06:20:57Z"
        //     }
        //
        $timestamp = $this->parse8601($this->safe_string_2($trade, 'created_at', 'inserted_at'));
        $priceString = $this->safe_string($trade, 'price');
        $amountString = $this->safe_string($trade, 'amount');
        $marketId = $this->safe_string($trade, 'asset_pair_name');
        $market = $this->safe_market($marketId, $market, '-');
        $side = $this->safe_string($trade, 'side');
        $takerSide = $this->safe_string($trade, 'taker_side');
        $takerOrMaker = null;
        if (($takerSide !== null) && ($side !== null) && ($side !== 'SELF_TRADING')) {
            $takerOrMaker = ($takerSide === $side) ? 'taker' : 'maker';
        }
        if ($side === null) {
            // taker $side is not related to buy/sell $side
            // the following code is probably a mistake
            $side = ($takerSide === 'ASK') ? 'sell' : 'buy';
        } else {
            if ($side === 'BID') {
                $side = 'buy';
            } elseif ($side === 'ASK') {
                $side = 'sell';
            }
        }
        $makerOrderId = $this->safe_string($trade, 'maker_order_id');
        $takerOrderId = $this->safe_string($trade, 'taker_order_id');
        $orderId = null;
        if ($makerOrderId !== null) {
            $orderId = $makerOrderId;
        } elseif ($takerOrderId !== null) {
            $orderId = $takerOrderId;
        }
        $id = $this->safe_string($trade, 'id');
        $result = array(
            'id' => $id,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $market['symbol'],
            'order' => $orderId,
            'type' => 'limit',
            'side' => $side,
            'takerOrMaker' => $takerOrMaker,
            'price' => $priceString,
            'amount' => $amountString,
            'cost' => null,
            'info' => $trade,
        );
        if ($takerOrMaker !== null) {
            if ($side === 'buy') {
                if ($takerOrMaker === 'maker') {
                    $makerCurrencyCode = $market['base'];
                    $takerCurrencyCode = $market['quote'];
                } else {
                    $makerCurrencyCode = $market['quote'];
                    $takerCurrencyCode = $market['base'];
                }
            } else {
                if ($takerOrMaker === 'maker') {
                    $makerCurrencyCode = $market['quote'];
                    $takerCurrencyCode = $market['base'];
                } else {
                    $makerCurrencyCode = $market['base'];
                    $takerCurrencyCode = $market['quote'];
                }
            }
        } elseif ($side === 'SELF_TRADING') {
            if ($takerSide === 'BID') {
                $makerCurrencyCode = $market['quote'];
                $takerCurrencyCode = $market['base'];
            } elseif ($takerSide === 'ASK') {
                $makerCurrencyCode = $market['base'];
                $takerCurrencyCode = $market['quote'];
            }
        }
        $makerFeeCost = $this->safe_string($trade, 'maker_fee');
        $takerFeeCost = $this->safe_string($trade, 'taker_fee');
        if ($makerFeeCost !== null) {
            if ($takerFeeCost !== null) {
                $result['fees'] = array(
                    array( 'cost' => $makerFeeCost, 'currency' => $makerCurrencyCode ),
                    array( 'cost' => $takerFeeCost, 'currency' => $takerCurrencyCode ),
                );
            } else {
                $result['fee'] = array( 'cost' => $makerFeeCost, 'currency' => $makerCurrencyCode );
            }
        } elseif ($takerFeeCost !== null) {
            $result['fee'] = array( 'cost' => $takerFeeCost, 'currency' => $takerCurrencyCode );
        } else {
            $result['fee'] = null;
        }
        return $this->safe_trade($result, $market);
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * get the list of most recent $trades for a particular $symbol
         *
         * @see https://open.big.one/docs/spot_asset_pair_trade.html
         *
         * @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~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        if ($market['contract']) {
            throw new NotSupported($this->id . ' fetchTrades () can only fetch $trades for spot markets');
        }
        $request = array(
            'asset_pair_name' => $market['id'],
        );
        $response = $this->publicGetAssetPairsAssetPairNameTrades ($this->extend($request, $params));
        //
        //     {
        //         "code" => 0,
        //         "data" => array(
        //             array(
        //                 "id" => 38199941,
        //                 "price" => "3378.67",
        //                 "amount" => "0.019812",
        //                 "taker_side" => "ASK",
        //                 "created_at" => "2019-01-29T06:05:56Z"
        //             ),
        //             {
        //                 "id" => 38199934,
        //                 "price" => "3376.14",
        //                 "amount" => "0.019384",
        //                 "taker_side" => "ASK",
        //                 "created_at" => "2019-01-29T06:05:40Z"
        //             }
        //         )
        //     }
        //
        $trades = $this->safe_list($response, 'data', array());
        return $this->parse_trades($trades, $market, $since, $limit);
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        //
        //     {
        //         "close" => "0.021562",
        //         "high" => "0.021563",
        //         "low" => "0.02156",
        //         "open" => "0.021563",
        //         "time" => "2019-11-21T07:54:00Z",
        //         "volume" => "59.84376"
        //     }
        //
        return array(
            $this->parse8601($this->safe_string($ohlcv, 'time')),
            $this->safe_number($ohlcv, 'open'),
            $this->safe_number($ohlcv, 'high'),
            $this->safe_number($ohlcv, 'low'),
            $this->safe_number($ohlcv, 'close'),
            $this->safe_number($ohlcv, 'volume'),
        );
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches historical candlestick $data containing the open, high, low, and close price, and the volume of a $market
         *
         * @see https://open.big.one/docs/spot_asset_pair_candle.html
         *
         * @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
         * @param {int} [$params->until] timestamp in ms of the earliest candle to fetch
         * @return {int[][]} A list of candles ordered, open, high, low, close, volume
         */
        $this->load_markets();
        $market = $this->market($symbol);
        if ($market['contract']) {
            throw new NotSupported($this->id . ' fetchOHLCV () can only fetch ohlcvs for spot markets');
        }
        $until = $this->safe_integer($params, 'until');
        $untilIsDefined = ($until !== null);
        $sinceIsDefined = ($since !== null);
        if ($limit === null) {
            $limit = ($sinceIsDefined && $untilIsDefined) ? 500 : 100; // default 100, max 500, if $since and $limit defined then fetch all the candles between them unless it exceeds the max of 500
        }
        $request = array(
            'asset_pair_name' => $market['id'],
            'period' => $this->safe_string($this->timeframes, $timeframe, $timeframe),
            'limit' => $limit,
        );
        if ($sinceIsDefined) {
            // $start = $this->parse_to_int($since / 1000);
            $duration = $this->parse_timeframe($timeframe);
            $endByLimit = $this->sum($since, $limit * $duration * 1000);
            if ($untilIsDefined) {
                $request['time'] = $this->iso8601(min ($endByLimit, $until + 1));
            } else {
                $request['time'] = $this->iso8601($endByLimit);
            }
        } elseif ($untilIsDefined) {
            $request['time'] = $this->iso8601($until + 1);
        }
        $params = $this->omit($params, 'until');
        $response = $this->publicGetAssetPairsAssetPairNameCandles ($this->extend($request, $params));
        //
        //     {
        //         "code" => 0,
        //         "data" => array(
        //             array(
        //                 "close" => "0.021656",
        //                 "high" => "0.021658",
        //                 "low" => "0.021652",
        //                 "open" => "0.021652",
        //                 "time" => "2019-11-21T09:30:00Z",
        //                 "volume" => "53.08664"
        //             ),
        //             array(
        //                 "close" => "0.021652",
        //                 "high" => "0.021656",
        //                 "low" => "0.021652",
        //                 "open" => "0.021656",
        //                 "time" => "2019-11-21T09:29:00Z",
        //                 "volume" => "88.39861"
        //             ),
        //         )
        //     }
        //
        $data = $this->safe_list($response, 'data', array());
        return $this->parse_ohlcvs($data, $market, $timeframe, $since, $limit);
    }

    public function parse_balance($response): array {
        $result = array(
            'info' => $response,
            'timestamp' => null,
            'datetime' => null,
        );
        $balances = $this->safe_list($response, 'data', array());
        for ($i = 0; $i < count($balances); $i++) {
            $balance = $balances[$i];
            $symbol = $this->safe_string($balance, 'asset_symbol');
            $code = $this->safe_currency_code($symbol);
            $account = $this->account();
            $account['total'] = $this->safe_string($balance, 'balance');
            $account['used'] = $this->safe_string($balance, 'locked_balance');
            $result[$code] = $account;
        }
        return $this->safe_balance($result);
    }

    public function fetch_balance($params = array ()): array {
        /**
         * query for balance and get the amount of funds available for trading or funds locked in orders
         *
         * @see https://open.big.one/docs/fund_accounts.html
         * @see https://open.big.one/docs/spot_accounts.html
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
         */
        $this->load_markets();
        $type = $this->safe_string($params, 'type', '');
        $params = $this->omit($params, 'type');
        $response = null;
        if ($type === 'funding' || $type === 'fund') {
            $response = $this->privateGetFundAccounts ($params);
        } else {
            $response = $this->privateGetAccounts ($params);
        }
        //
        //     {
        //         "code":0,
        //         "data":array(
        //             array("asset_symbol":"NKC","balance":"0","locked_balance":"0"),
        //             array("asset_symbol":"UBTC","balance":"0","locked_balance":"0"),
        //             array("asset_symbol":"READ","balance":"0","locked_balance":"0"),
        //         ),
        //     }
        //
        return $this->parse_balance($response);
    }

    public function parse_type(string $type) {
        $types = array(
            'STOP_LIMIT' => 'limit',
            'STOP_MARKET' => 'market',
            'LIMIT' => 'limit',
            'MARKET' => 'market',
        );
        return $this->safe_string($types, $type, $type);
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        //    {
        //        "id" => "42154072251",
        //        "asset_pair_name" => "SOL-USDT",
        //        "price" => "20",
        //        "amount" => "0.5",
        //        "filled_amount" => "0",
        //        "avg_deal_price" => "0",
        //        "side" => "ASK",
        //        "state" => "PENDING",
        //        "created_at" => "2023-09-13T03:42:00Z",
        //        "updated_at" => "2023-09-13T03:42:00Z",
        //        "type" => "LIMIT",
        //        "stop_price" => "0",
        //        "immediate_or_cancel" => false,
        //        "post_only" => false,
        //        "client_order_id" => ''
        //    }
        //
        $id = $this->safe_string($order, 'id');
        $marketId = $this->safe_string($order, 'asset_pair_name');
        $symbol = $this->safe_symbol($marketId, $market, '-');
        $timestamp = $this->parse8601($this->safe_string($order, 'created_at'));
        $side = $this->safe_string($order, 'side');
        if ($side === 'BID') {
            $side = 'buy';
        } else {
            $side = 'sell';
        }
        $triggerPrice = $this->safe_string($order, 'stop_price');
        if (Precise::string_eq($triggerPrice, '0')) {
            $triggerPrice = null;
        }
        $immediateOrCancel = $this->safe_bool($order, 'immediate_or_cancel');
        $timeInForce = null;
        if ($immediateOrCancel) {
            $timeInForce = 'IOC';
        }
        $type = $this->parse_type($this->safe_string($order, 'type'));
        $price = $this->safe_string($order, 'price');
        $amount = null;
        $filled = null;
        $cost = null;
        if ($type === 'market' && $side === 'buy') {
            $cost = $this->safe_string($order, 'filled_amount');
        } else {
            $amount = $this->safe_string($order, 'amount');
            $filled = $this->safe_string($order, 'filled_amount');
        }
        return $this->safe_order(array(
            'info' => $order,
            'id' => $id,
            'clientOrderId' => $this->safe_string($order, 'client_order_id'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => $this->parse8601($this->safe_string($order, 'updated_at')),
            'symbol' => $symbol,
            'type' => $type,
            'timeInForce' => $timeInForce,
            'postOnly' => $this->safe_bool($order, 'post_only'),
            'side' => $side,
            'price' => $price,
            'triggerPrice' => $triggerPrice,
            'amount' => $amount,
            'cost' => $cost,
            'average' => $this->safe_string($order, 'avg_deal_price'),
            'filled' => $filled,
            'remaining' => null,
            'status' => $this->parse_order_status($this->safe_string($order, 'state')),
            'fee' => null,
            'trades' => null,
        ), $market);
    }

    public function create_market_buy_order_with_cost(string $symbol, float $cost, $params = array ()) {
        /**
         * create a $market buy order by providing the $symbol and $cost
         *
         * @see https://open.big.one/docs/spot_orders.html#create-order
         *
         * @param {string} $symbol unified $symbol of the $market to create an order in
         * @param {float} $cost how much you want to trade in units of the quote currency
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        if (!$market['spot']) {
            throw new NotSupported($this->id . ' createMarketBuyOrderWithCost() supports spot orders only');
        }
        $params['createMarketBuyOrderRequiresPrice'] = false;
        return $this->create_order($symbol, 'market', 'buy', $cost, null, $params);
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        /**
         * create a trade $order
         *
         * @see https://open.big.one/docs/spot_orders.html#create-$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
         * @param {float} [$params->triggerPrice] the $price at which a trigger $order is triggered at
         * @param {bool} [$params->postOnly] if true, the $order will only be posted to the $order book and not executed immediately
         * @param {string} [$params->timeInForce] "GTC", "IOC", or "PO"
         * @param {float} [$params->cost] *spot $market buy only* the quote quantity that can be used alternative for the $amount
         *
         * EXCHANGE SPECIFIC PARAMETERS
         * @param {string} [$params->operator] *stop $order only* GTE or LTE (default)
         * @param {string} [$params->client_order_id] must match ^[a-zA-Z0-9-_]array(1,36)$ this regex. client_order_id is unique in 24 hours, If created 24 hours later and the $order closed, it will be released and can be reused
         * @return {array} an ~@link https://docs.ccxt.com/#/?id=$order-structure $order structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $isBuy = ($side === 'buy');
        $requestSide = $isBuy ? 'BID' : 'ASK';
        $uppercaseType = strtoupper($type);
        $isLimit = $uppercaseType === 'LIMIT';
        $exchangeSpecificParam = $this->safe_bool($params, 'post_only', false);
        $postOnly = null;
        list($postOnly, $params) = $this->handle_post_only(($uppercaseType === 'MARKET'), $exchangeSpecificParam, $params);
        $triggerPrice = $this->safe_string_n($params, array( 'triggerPrice', 'stopPrice', 'stop_price' ));
        $request = array(
            'asset_pair_name' => $market['id'], // asset pair name BTC-USDT, required
            'side' => $requestSide, // $order $side one of "ASK"/"BID", required
            'amount' => $this->amount_to_precision($symbol, $amount), // $order $amount, string, required
            // "price" => $this->price_to_precision($symbol, $price), // $order $price, string, required
            // "operator" => "GTE", // stop orders only, GTE greater than and equal, LTE less than and equal
            // "immediate_or_cancel" => false, // limit orders only, must be false when post_only is true
            // "post_only" => false, // limit orders only, must be false when immediate_or_cancel is true
        );
        if ($isLimit || ($uppercaseType === 'STOP_LIMIT')) {
            $request['price'] = $this->price_to_precision($symbol, $price);
            if ($isLimit) {
                $timeInForce = $this->safe_string($params, 'timeInForce');
                if ($timeInForce === 'IOC') {
                    $request['immediate_or_cancel'] = true;
                }
                if ($postOnly) {
                    $request['post_only'] = true;
                }
            }
            $request['amount'] = $this->amount_to_precision($symbol, $amount);
        } else {
            if ($isBuy) {
                $createMarketBuyOrderRequiresPrice = true;
                list($createMarketBuyOrderRequiresPrice, $params) = $this->handle_option_and_params($params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true);
                $cost = $this->safe_number($params, 'cost');
                $params = $this->omit($params, 'cost');
                if ($createMarketBuyOrderRequiresPrice) {
                    if (($price === null) && ($cost === null)) {
                        throw new InvalidOrder($this->id . ' createOrder() requires the $price argument for $market buy orders to calculate the total $cost to spend ($amount * $price), alternatively set the $createMarketBuyOrderRequiresPrice option or param to false and pass the $cost to spend in the $amount argument');
                    } else {
                        $amountString = $this->number_to_string($amount);
                        $priceString = $this->number_to_string($price);
                        $quoteAmount = $this->parse_to_numeric(Precise::string_mul($amountString, $priceString));
                        $costRequest = ($cost !== null) ? $cost : $quoteAmount;
                        $request['amount'] = $this->cost_to_precision($symbol, $costRequest);
                    }
                } else {
                    $request['amount'] = $this->cost_to_precision($symbol, $amount);
                }
            } else {
                $request['amount'] = $this->amount_to_precision($symbol, $amount);
            }
        }
        if ($triggerPrice !== null) {
            $request['stop_price'] = $this->price_to_precision($symbol, $triggerPrice);
            $request['operator'] = $isBuy ? 'GTE' : 'LTE';
            if ($isLimit) {
                $uppercaseType = 'STOP_LIMIT';
            } elseif ($uppercaseType === 'MARKET') {
                $uppercaseType = 'STOP_MARKET';
            }
        }
        $request['type'] = $uppercaseType;
        $clientOrderId = $this->safe_string($params, 'clientOrderId');
        if ($clientOrderId !== null) {
            $request['client_order_id'] = $clientOrderId;
        }
        $params = $this->omit($params, array( 'stop_price', 'stopPrice', 'triggerPrice', 'timeInForce', 'clientOrderId' ));
        $response = $this->privatePostOrders ($this->extend($request, $params));
        //
        //    {
        //        "id" => 10,
        //        "asset_pair_name" => "EOS-BTC",
        //        "price" => "10.00",
        //        "amount" => "10.00",
        //        "filled_amount" => "9.0",
        //        "avg_deal_price" => "12.0",
        //        "side" => "ASK",
        //        "state" => "FILLED",
        //        "created_at":"2019-01-29T06:05:56Z",
        //        "updated_at":"2019-01-29T06:05:56Z"
        //    }
        //
        $order = $this->safe_dict($response, 'data');
        return $this->parse_order($order, $market);
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        /**
         * cancels an open $order
         *
         * @see https://open.big.one/docs/spot_orders.html#cancel-$order
         *
         * @param {string} $id $order $id
         * @param {string} $symbol Not used by bigone cancelOrder ()
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} An ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
         */
        $this->load_markets();
        $request = array( 'id' => $id );
        $response = $this->privatePostOrdersIdCancel ($this->extend($request, $params));
        //    {
        //        "id" => 10,
        //        "asset_pair_name" => "EOS-BTC",
        //        "price" => "10.00",
        //        "amount" => "10.00",
        //        "filled_amount" => "9.0",
        //        "avg_deal_price" => "12.0",
        //        "side" => "ASK",
        //        "state" => "CANCELLED",
        //        "created_at":"2019-01-29T06:05:56Z",
        //        "updated_at":"2019-01-29T06:05:56Z"
        //    }
        $order = $this->safe_dict($response, 'data');
        return $this->parse_order($order);
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        /**
         * cancel all open orders
         *
         * @see https://open.big.one/docs/spot_orders.html#cancel-all-orders
         *
         * @param {string} $symbol unified $market $symbol, only orders in the $market of this $symbol are $cancelled when $symbol is not null
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'asset_pair_name' => $market['id'],
        );
        $response = $this->privatePostOrdersCancel ($this->extend($request, $params));
        //
        //     {
        //         "code":0,
        //         "data" => {
        //             "cancelled":array(
        //                 58272370,
        //                 58272377
        //             ),
        //             "failed" => array()
        //         }
        //     }
        //
        $data = $this->safe_dict($response, 'data', array());
        $cancelled = $this->safe_list($data, 'cancelled', array());
        $failed = $this->safe_list($data, 'failed', array());
        $result = array();
        for ($i = 0; $i < count($cancelled); $i++) {
            $orderId = $cancelled[$i];
            $result[] = $this->safe_order(array(
                'info' => $orderId,
                'id' => $orderId,
                'status' => 'canceled',
            ));
        }
        for ($i = 0; $i < count($failed); $i++) {
            $orderId = $failed[$i];
            $result[] = $this->safe_order(array(
                'info' => $orderId,
                'id' => $orderId,
                'status' => 'failed',
            ));
        }
        return $result;
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()) {
        /**
         * fetches information on an $order made by the user
         *
         * @see https://open.big.one/docs/spot_orders.html#get-one-$order
         *
         * @param {string} $id the $order $id
         * @param {string} $symbol not used by bigone fetchOrder
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} An ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
         */
        $this->load_markets();
        $request = array( 'id' => $id );
        $response = $this->privateGetOrdersId ($this->extend($request, $params));
        $order = $this->safe_dict($response, 'data', array());
        return $this->parse_order($order);
    }

    public function fetch_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on multiple $orders made by the user
         *
         * @see https://open.big.one/docs/spot_orders.html#get-user-$orders-in-one-asset-pair
         *
         * @param {string} $symbol unified $market $symbol of the $market $orders were made in
         * @param {int} [$since] the earliest time in ms to fetch $orders for
         * @param {int} [$limit] the maximum number of order 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~
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' fetchOrders() requires a $symbol argument');
        }
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'asset_pair_name' => $market['id'],
            // 'page_token' => 'dxzef', // $request page after this page token
            // 'side' => 'ASK', // 'ASK' or 'BID', optional
            // 'state' => 'FILLED', // 'CANCELLED', 'FILLED', 'PENDING'
            // 'limit' 20, // default 20, max 200
        );
        if ($limit !== null) {
            $request['limit'] = $limit; // default 20, max 200
        }
        $response = $this->privateGetOrders ($this->extend($request, $params));
        //
        //    {
        //        "code":0,
        //        "data" => array(
        //             array(
        //                 "id" => 10,
        //                 "asset_pair_name" => "ETH-BTC",
        //                 "price" => "10.00",
        //                 "amount" => "10.00",
        //                 "filled_amount" => "9.0",
        //                 "avg_deal_price" => "12.0",
        //                 "side" => "ASK",
        //                 "state" => "FILLED",
        //                 "created_at":"2019-01-29T06:05:56Z",
        //                 "updated_at":"2019-01-29T06:05:56Z",
        //             ),
        //         ),
        //        "page_token":"dxzef",
        //    }
        //
        $orders = $this->safe_list($response, 'data', array());
        return $this->parse_orders($orders, $market, $since, $limit);
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        /**
         * fetch all $trades made by the user
         *
         * @see https://open.big.one/docs/spot_trade.html#$trades-of-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~
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' fetchMyTrades() requires a $symbol argument');
        }
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'asset_pair_name' => $market['id'],
            // 'page_token' => 'dxzef', // $request page after this page token
        );
        if ($limit !== null) {
            $request['limit'] = $limit; // default 20, max 200
        }
        $response = $this->privateGetTrades ($this->extend($request, $params));
        //
        //     {
        //         "code" => 0,
        //         "data" => array(
        //             array(
        //                 "id" => 10854280,
        //                 "asset_pair_name" => "XIN-USDT",
        //                 "price" => "70",
        //                 "amount" => "1",
        //                 "taker_side" => "ASK",
        //                 "maker_order_id" => 58284908,
        //                 "taker_order_id" => 58284909,
        //                 "maker_fee" => "0.0008",
        //                 "taker_fee" => "0.07",
        //                 "side" => "SELF_TRADING",
        //                 "inserted_at" => "2019-04-16T12:00:01Z"
        //             ),
        //             {
        //                 "id" => 10854263,
        //                 "asset_pair_name" => "XIN-USDT",
        //                 "price" => "75.7",
        //                 "amount" => "12.743149",
        //                 "taker_side" => "BID",
        //                 "maker_order_id" => null,
        //                 "taker_order_id" => 58284888,
        //                 "maker_fee" => null,
        //                 "taker_fee" => "0.0025486298",
        //                 "side" => "BID",
        //                 "inserted_at" => "2019-04-15T06:20:57Z"
        //             }
        //         ),
        //         "page_token":"dxfv"
        //     }
        //
        $trades = $this->safe_list($response, 'data', array());
        return $this->parse_trades($trades, $market, $since, $limit);
    }

    public function parse_order_status(?string $status) {
        $statuses = array(
            'PENDING' => 'open',
            'FILLED' => 'closed',
            'CANCELLED' => 'canceled',
        );
        return $this->safe_string($statuses, $status);
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch all unfilled currently open orders
         *
         * @see https://open.big.one/docs/spot_orders.html#get-user-orders-in-one-asset-pair
         *
         * @param {string} $symbol unified market $symbol
         * @param {int} [$since] the earliest time in ms to fetch open orders for
         * @param {int} [$limit] the maximum number of  open orders structures to retrieve
         * @param {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~
         */
        $request = array(
            'state' => 'PENDING',
        );
        return $this->fetch_orders($symbol, $since, $limit, $this->extend($request, $params));
    }

    public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on multiple closed orders made by the user
         *
         * @see https://open.big.one/docs/spot_orders.html#get-user-orders-in-one-asset-pair
         *
         * @param {string} $symbol unified market $symbol of the market orders were made in
         * @param {int} [$since] the earliest time in ms to fetch orders for
         * @param {int} [$limit] the maximum number of order 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~
         */
        $request = array(
            'state' => 'FILLED',
        );
        return $this->fetch_orders($symbol, $since, $limit, $this->extend($request, $params));
    }

    public function nonce() {
        $exchangeTimeCorrection = $this->safe_integer($this->options, 'exchangeMillisecondsCorrection', 0) * 1000000;
        return $this->sum($this->microseconds() * 1000, $exchangeTimeCorrection);
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $query = $this->omit($params, $this->extract_params($path));
        $baseUrl = $this->implode_hostname($this->urls['api'][$api]);
        $url = $baseUrl . '/' . $this->implode_params($path, $params);
        $headers = array();
        if ($api === 'public' || $api === 'webExchange' || $api === 'contractPublic') {
            if ($query) {
                $url .= '?' . $this->urlencode($query);
            }
        } else {
            $this->check_required_credentials();
            $nonce = (string) $this->nonce();
            $request = array(
                'type' => 'OpenAPIV2',
                'sub' => $this->apiKey,
                'nonce' => $nonce,
                // 'recv_window' => '30', // default 30
            );
            $token = $this->jwt($request, $this->encode($this->secret), 'sha256');
            $headers['Authorization'] = 'Bearer ' . $token;
            if ($method === 'GET') {
                if ($query) {
                    $url .= '?' . $this->urlencode($query);
                }
            } elseif ($method === 'POST') {
                $headers['Content-Type'] = 'application/json';
                $body = $this->json($query);
            }
        }
        $headers['User-Agent'] = 'ccxt/' . $this->id . '-' . $this->version;
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function fetch_deposit_address(string $code, $params = array ()): array {
        /**
         * fetch the deposit $address for a $currency associated with this account
         *
         * @see https://open.big.one/docs/spot_deposit.html#get-deposite-$address-of-one-asset-of-user
         *
         * @param {string} $code unified $currency $code
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} an ~@link https://docs.ccxt.com/#/?id=$address-structure $address structure~
         */
        $this->load_markets();
        $currency = $this->currency($code);
        $request = array(
            'asset_symbol' => $currency['id'],
        );
        list($networkCode, $paramsOmitted) = $this->handle_network_code_and_params($params);
        $response = $this->privateGetAssetsAssetSymbolAddress ($this->extend($request, $paramsOmitted));
        //
        // the actual $response format is not the same documented one
        // the $data key contains an array in the actual $response
        //
        //     {
        //         "code":0,
        //         "message":"",
        //         "data":array(
        //             {
        //                 "id":5521878,
        //                 "chain":"Bitcoin",
        //                 "value":"1GbmyKoikhpiQVZ1C9sbF17mTyvBjeobVe",
        //                 "memo":""
        //             }
        //         )
        //     }
        //
        $data = $this->safe_list($response, 'data', array());
        $dataLength = count($data);
        if ($dataLength < 1) {
            throw new ExchangeError($this->id . ' fetchDepositAddress() returned empty $address response');
        }
        $chainsIndexedById = $this->index_by($data, 'chain');
        $selectedNetworkId = $this->select_network_id_from_raw_networks($code, $networkCode, $chainsIndexedById);
        $addressObject = $this->safe_dict($chainsIndexedById, $selectedNetworkId, array());
        $address = $this->safe_string($addressObject, 'value');
        $tag = $this->safe_string($addressObject, 'memo');
        $this->check_address($address);
        return array(
            'info' => $response,
            'currency' => $code,
            'network' => $this->network_id_to_code($selectedNetworkId),
            'address' => $address,
            'tag' => $tag,
        );
    }

    public function parse_transaction_status(?string $status) {
        $statuses = array(
            // what are other $statuses here?
            'WITHHOLD' => 'ok', // deposits
            'UNCONFIRMED' => 'pending',
            'CONFIRMED' => 'ok', // withdrawals
            'COMPLETED' => 'ok',
            'PENDING' => 'pending',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // fetchDeposits
        //
        //     {
        //         "amount" => "25.0",
        //         "asset_symbol" => "BTS"
        //         "confirms" => 100,
        //         "id" => 5,
        //         "inserted_at" => "2018-02-16T11:39:58.000Z",
        //         "is_internal" => false,
        //         "kind" => "default",
        //         "memo" => "",
        //         "state" => "WITHHOLD",
        //         "txid" => "72e03037d144dae3d32b68b5045462b1049a0755",
        //         "updated_at" => "2018-11-09T10:20:09.000Z",
        //     }
        //
        // fetchWithdrawals
        //
        //     {
        //         "amount" => "5",
        //         "asset_symbol" => "ETH",
        //         "completed_at" => "2018-03-15T16:13:45.610463Z",
        //         "customer_id" => "10",
        //         "id" => 10,
        //         "inserted_at" => "2018-03-15T16:13:45.610463Z",
        //         "is_internal" => true,
        //         "note" => "2018-03-15T16:13:45.610463Z",
        //         "state" => "CONFIRMED",
        //         "target_address" => "0x4643bb6b393ac20a6175c713175734a72517c63d6f7"
        //         "txid" => "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0",
        //     }
        //
        // withdraw
        //
        //     {
        //         "id":1077391,
        //         "customer_id":1082679,
        //         "amount":"21.9000000000000000",
        //         "txid":"",
        //         "is_internal":false,
        //         "kind":"on_chain",
        //         "state":"PENDING",
        //         "inserted_at":"2020-06-03T00:50:57+00:00",
        //         "updated_at":"2020-06-03T00:50:57+00:00",
        //         "memo":"",
        //         "target_address":"rDYtYT3dBeuw376rvHqoZBKW3UmvguoBAf",
        //         "fee":"0.1000000000000000",
        //         "asset_symbol":"XRP"
        //     }
        //
        $currencyId = $this->safe_string($transaction, 'asset_symbol');
        $code = $this->safe_currency_code($currencyId);
        $id = $this->safe_string($transaction, 'id');
        $amount = $this->safe_number($transaction, 'amount');
        $status = $this->parse_transaction_status($this->safe_string($transaction, 'state'));
        $timestamp = $this->parse8601($this->safe_string($transaction, 'inserted_at'));
        $updated = $this->parse8601($this->safe_string_2($transaction, 'updated_at', 'completed_at'));
        $txid = $this->safe_string($transaction, 'txid');
        $address = $this->safe_string($transaction, 'target_address');
        $tag = $this->safe_string($transaction, 'memo');
        $type = (is_array($transaction) && array_key_exists('customer_id', $transaction)) ? 'withdrawal' : 'deposit';
        $internal = $this->safe_bool($transaction, 'is_internal');
        return array(
            'info' => $transaction,
            'id' => $id,
            'txid' => $txid,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'network' => null,
            'addressFrom' => null,
            'address' => null,
            'addressTo' => $address,
            'tagFrom' => null,
            'tag' => $tag,
            'tagTo' => null,
            'type' => $type,
            'amount' => $amount,
            'currency' => $code,
            'status' => $status,
            'updated' => $updated,
            'fee' => null,
            'comment' => null,
            'internal' => $internal,
        );
    }

    public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch all $deposits made to an account
         *
         * @see https://open.big.one/docs/spot_deposit.html#deposit-of-user
         *
         * @param {string} $code unified $currency $code
         * @param {int} [$since] the earliest time in ms to fetch $deposits for
         * @param {int} [$limit] the maximum number of $deposits structures to retrieve
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
         */
        $this->load_markets();
        $request = array(
            // 'page_token' => 'dxzef', // $request page after this page token
            // 'limit' => 50, // optional, default 50
            // 'kind' => 'string', // optional - air_drop, big_holder_dividend, default, eosc_to_eos, internal, equally_airdrop, referral_mining, one_holder_dividend, single_customer, snapshotted_airdrop, trade_mining
            // 'asset_symbol' => 'BTC', // optional
        );
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
            $request['asset_symbol'] = $currency['id'];
        }
        if ($limit !== null) {
            $request['limit'] = $limit; // default 50
        }
        $response = $this->privateGetDeposits ($this->extend($request, $params));
        //
        //     {
        //         "code" => 0,
        //         "page_token" => "NQ==",
        //         "data" => array(
        //             {
        //                 "id" => 5,
        //                 "amount" => "25.0",
        //                 "confirms" => 100,
        //                 "txid" => "72e03037d144dae3d32b68b5045462b1049a0755",
        //                 "is_internal" => false,
        //                 "inserted_at" => "2018-02-16T11:39:58.000Z",
        //                 "updated_at" => "2018-11-09T10:20:09.000Z",
        //                 "kind" => "default",
        //                 "memo" => "",
        //                 "state" => "WITHHOLD",
        //                 "asset_symbol" => "BTS"
        //             }
        //         )
        //     }
        //
        $deposits = $this->safe_list($response, 'data', array());
        return $this->parse_transactions($deposits, $currency, $since, $limit);
    }

    public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch all $withdrawals made from an account
         *
         * @see https://open.big.one/docs/spot_withdrawal.html#get-$withdrawals-of-user
         *
         * @param {string} $code unified $currency $code
         * @param {int} [$since] the earliest time in ms to fetch $withdrawals for
         * @param {int} [$limit] the maximum number of $withdrawals structures to retrieve
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
         */
        $this->load_markets();
        $request = array(
            // 'page_token' => 'dxzef', // $request page after this page token
            // 'limit' => 50, // optional, default 50
            // 'kind' => 'string', // optional - air_drop, big_holder_dividend, default, eosc_to_eos, internal, equally_airdrop, referral_mining, one_holder_dividend, single_customer, snapshotted_airdrop, trade_mining
            // 'asset_symbol' => 'BTC', // optional
        );
        $currency = null;
        if ($code !== null) {
            $currency = $this->currency($code);
            $request['asset_symbol'] = $currency['id'];
        }
        if ($limit !== null) {
            $request['limit'] = $limit; // default 50
        }
        $response = $this->privateGetWithdrawals ($this->extend($request, $params));
        //
        //     {
        //         "code" => 0,
        //         "data" => array(
        //             {
        //                 "id" => 10,
        //                 "customer_id" => "10",
        //                 "asset_symbol" => "ETH",
        //                 "amount" => "5",
        //                 "state" => "CONFIRMED",
        //                 "note" => "2018-03-15T16:13:45.610463Z",
        //                 "txid" => "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0",
        //                 "completed_at" => "2018-03-15T16:13:45.610463Z",
        //                 "inserted_at" => "2018-03-15T16:13:45.610463Z",
        //                 "is_internal" => true,
        //                 "target_address" => "0x4643bb6b393ac20a6175c713175734a72517c63d6f7"
        //             }
        //         ),
        //         "page_token":"dxvf"
        //     }
        //
        $withdrawals = $this->safe_list($response, 'data', array());
        return $this->parse_transactions($withdrawals, $currency, $since, $limit);
    }

    public function transfer(string $code, float $amount, string $fromAccount, string $toAccount, $params = array ()): array {
        /**
         * $transfer $currency internally between wallets on the same account
         *
         * @see https://open.big.one/docs/spot_transfer.html#$transfer-of-user
         *
         * @param {string} $code unified $currency $code
         * @param {float} $amount amount to $transfer
         * @param {string} $fromAccount 'SPOT', 'FUND', or 'CONTRACT'
         * @param {string} $toAccount 'SPOT', 'FUND', or 'CONTRACT'
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=$transfer-structure $transfer structure~
         */
        $this->load_markets();
        $currency = $this->currency($code);
        $accountsByType = $this->safe_dict($this->options, 'accountsByType', array());
        $fromId = $this->safe_string($accountsByType, $fromAccount, $fromAccount);
        $toId = $this->safe_string($accountsByType, $toAccount, $toAccount);
        $guid = $this->safe_string($params, 'guid', $this->uuid());
        $request = array(
            'symbol' => $currency['id'],
            'amount' => $this->currency_to_precision($code, $amount),
            'from' => $fromId,
            'to' => $toId,
            'guid' => $guid,
            // 'type' => type, // NORMAL, MASTER_TO_SUB, SUB_TO_MASTER, SUB_INTERNAL, default is NORMAL
            // 'sub_acccunt' => '', // when type is NORMAL, it should be empty, and when type is others it is required
        );
        $response = $this->privatePostTransfer ($this->extend($request, $params));
        //
        //     {
        //         "code" => 0,
        //         "data" => null
        //     }
        //
        $transfer = $this->parse_transfer($response, $currency);
        $transferOptions = $this->safe_dict($this->options, 'transfer', array());
        $fillResponseFromRequest = $this->safe_bool($transferOptions, 'fillResponseFromRequest', true);
        if ($fillResponseFromRequest) {
            $transfer['fromAccount'] = $fromAccount;
            $transfer['toAccount'] = $toAccount;
            $transfer['amount'] = $amount;
            $transfer['id'] = $guid;
        }
        return $transfer;
    }

    public function parse_transfer(array $transfer, ?array $currency = null): array {
        //
        //     {
        //         "code" => 0,
        //         "data" => null
        //     }
        //
        $code = $this->safe_string($transfer, 'code');
        return array(
            'info' => $transfer,
            'id' => null,
            'timestamp' => null,
            'datetime' => null,
            'currency' => null,
            'amount' => null,
            'fromAccount' => null,
            'toAccount' => null,
            'status' => $this->parse_transfer_status($code),
        );
    }

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

    public function withdraw(string $code, float $amount, string $address, ?string $tag = null, $params = array ()): array {
        /**
         * make a withdrawal
         *
         * @see https://open.big.one/docs/spot_withdrawal.html#create-withdrawal-of-user
         *
         * @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->load_markets();
        $currency = $this->currency($code);
        $request = array(
            'symbol' => $currency['id'],
            'target_address' => $address,
            'amount' => $this->currency_to_precision($code, $amount),
        );
        if ($tag !== null) {
            $request['memo'] = $tag;
        }
        $networkCode = null;
        list($networkCode, $params) = $this->handle_network_code_and_params($params);
        if ($networkCode !== null) {
            $request['gateway_name'] = $this->network_code_to_id($networkCode);
        }
        // requires write permission on the wallet
        $response = $this->privatePostWithdrawals ($this->extend($request, $params));
        //
        //     {
        //         "code":0,
        //         "message":"",
        //         "data":{
        //             "id":1077391,
        //             "customer_id":1082679,
        //             "amount":"21.9000000000000000",
        //             "txid":"",
        //             "is_internal":false,
        //             "kind":"on_chain",
        //             "state":"PENDING",
        //             "inserted_at":"2020-06-03T00:50:57+00:00",
        //             "updated_at":"2020-06-03T00:50:57+00:00",
        //             "memo":"",
        //             "target_address":"rDYtYT3dBeuw376rvHqoZBKW3UmvguoBAf",
        //             "fee":"0.1000000000000000",
        //             "asset_symbol":"XRP"
        //         }
        //     }
        //
        $data = $this->safe_dict($response, 'data', array());
        return $this->parse_transaction($data, $currency);
    }

    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
        }
        //
        //      array("code":10013,"message":"Resource not found")
        //      array("code":40004,"message":"invalid jwt")
        //
        $code = $this->safe_string($response, 'code');
        $message = $this->safe_string($response, 'message');
        if (($code !== '0') && ($code !== null)) {
            $feedback = $this->id . ' ' . $body;
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $message, $feedback);
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $code, $feedback);
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $message, $feedback);
            throw new ExchangeError($feedback); // unknown $message
        }
        return null;
    }
}
