<?php

namespace ccxt\async;

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

use Exception; // a common import
use ccxt\async\abstract\mexc as Exchange;
use ccxt\ExchangeError;
use ccxt\ArgumentsRequired;
use ccxt\BadRequest;
use ccxt\BadSymbol;
use ccxt\InvalidAddress;
use ccxt\InvalidOrder;
use ccxt\NotSupported;
use ccxt\Precise;
use \React\Async;
use \React\Promise;
use \React\Promise\PromiseInterface;

class mexc extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'mexc',
            'name' => 'MEXC Global',
            'countries' => array( 'SC' ), // Seychelles
            'rateLimit' => 50, // default rate limit is 20 times per second
            'version' => 'v3',
            'certified' => true,
            'pro' => true,
            'has' => array(
                'CORS' => null,
                'spot' => true,
                'margin' => true,
                'swap' => true,
                'future' => false,
                'option' => false,
                'addMargin' => true,
                'borrowCrossMargin' => false,
                'borrowIsolatedMargin' => false,
                'borrowMargin' => false,
                'cancelAllOrders' => true,
                'cancelOrder' => true,
                'cancelOrders' => null,
                'closeAllPositions' => false,
                'closePosition' => false,
                'createDepositAddress' => true,
                'createMarketBuyOrderWithCost' => true,
                'createMarketOrderWithCost' => true,
                'createMarketSellOrderWithCost' => true,
                'createOrder' => true,
                'createOrders' => true,
                'createPostOnlyOrder' => true,
                'createReduceOnlyOrder' => true,
                'createStopLimitOrder' => true,
                'createStopMarketOrder' => true,
                'createStopOrder' => true,
                'createTriggerOrder' => true,
                'deposit' => null,
                'editOrder' => null,
                'fetchAccounts' => true,
                'fetchBalance' => true,
                'fetchBidsAsks' => true,
                'fetchBorrowInterest' => false,
                'fetchBorrowRate' => false,
                'fetchBorrowRateHistories' => false,
                'fetchBorrowRateHistory' => false,
                'fetchBorrowRates' => false,
                'fetchBorrowRatesPerSymbol' => false,
                'fetchCanceledOrders' => true,
                'fetchClosedOrder' => null,
                'fetchClosedOrders' => true,
                'fetchCrossBorrowRate' => false,
                'fetchCrossBorrowRates' => false,
                'fetchCurrencies' => true,
                'fetchDeposit' => null,
                'fetchDepositAddress' => true,
                'fetchDepositAddresses' => null,
                'fetchDepositAddressesByNetwork' => true,
                'fetchDeposits' => true,
                'fetchDepositWithdrawFee' => 'emulated',
                'fetchDepositWithdrawFees' => true,
                'fetchFundingHistory' => true,
                'fetchFundingInterval' => true,
                'fetchFundingIntervals' => false,
                'fetchFundingRate' => true,
                'fetchFundingRateHistory' => true,
                'fetchFundingRates' => false,
                'fetchIndexOHLCV' => true,
                'fetchIsolatedBorrowRate' => false,
                'fetchIsolatedBorrowRates' => false,
                'fetchIsolatedPositions' => false,
                'fetchL2OrderBook' => true,
                'fetchLedger' => null,
                'fetchLedgerEntry' => null,
                'fetchLeverage' => true,
                'fetchLeverages' => false,
                'fetchLeverageTiers' => true,
                'fetchMarginAdjustmentHistory' => false,
                'fetchMarginMode' => false,
                'fetchMarketLeverageTiers' => 'emulated',
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => true,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterest' => false,
                'fetchOpenInterestHistory' => false,
                'fetchOpenOrder' => null,
                'fetchOpenOrders' => true,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrderBooks' => null,
                'fetchOrders' => true,
                'fetchOrderTrades' => true,
                'fetchPosition' => 'emulated',
                'fetchPositionHistory' => 'emulated',
                'fetchPositionMode' => true,
                'fetchPositions' => true,
                'fetchPositionsHistory' => true,
                'fetchPositionsRisk' => null,
                'fetchPremiumIndexOHLCV' => false,
                'fetchStatus' => true,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => true,
                'fetchTradingFees' => false,
                'fetchTradingLimits' => null,
                'fetchTransactionFee' => 'emulated',
                'fetchTransactionFees' => true,
                'fetchTransactions' => null,
                'fetchTransfer' => true,
                'fetchTransfers' => true,
                'fetchWithdrawal' => null,
                'fetchWithdrawals' => true,
                'reduceMargin' => true,
                'repayCrossMargin' => false,
                'repayIsolatedMargin' => false,
                'setLeverage' => true,
                'setMarginMode' => true,
                'setPositionMode' => true,
                'signIn' => null,
                'transfer' => null,
                'withdraw' => true,
            ),
            'urls' => array(
                'logo' => 'https://user-images.githubusercontent.com/1294454/137283979-8b2a818d-8633-461b-bfca-de89e8c446b2.jpg',
                'api' => array(
                    'spot' => array(
                        'public' => 'https://api.mexc.com',
                        'private' => 'https://api.mexc.com',
                    ),
                    'spot2' => array(
                        'public' => 'https://www.mexc.com/open/api/v2',
                        'private' => 'https://www.mexc.com/open/api/v2',
                    ),
                    'contract' => array(
                        'public' => 'https://contract.mexc.com/api/v1/contract',
                        'private' => 'https://contract.mexc.com/api/v1/private',
                    ),
                    'broker' => array(
                        'private' => 'https://api.mexc.com/api/v3/broker',
                    ),
                ),
                'www' => 'https://www.mexc.com/',
                'doc' => array(
                    'https://mexcdevelop.github.io/apidocs/',
                ),
                'fees' => array(
                    'https://www.mexc.com/fee',
                ),
                'referral' => 'https://www.mexc.com/register?inviteCode=mexc-1FQ1GNu1',
            ),
            'api' => array(
                'spot' => array(
                    'public' => array(
                        'get' => array(
                            'ping' => 1,
                            'time' => 1,
                            'exchangeInfo' => 10,
                            'depth' => 1,
                            'trades' => 5,
                            'historicalTrades' => 1,
                            'aggTrades' => 1,
                            'klines' => 1,
                            'avgPrice' => 1,
                            'ticker/24hr' => 1,
                            'ticker/price' => 1,
                            'ticker/bookTicker' => 1,
                            'etf/info' => 1,
                        ),
                    ),
                    'private' => array(
                        'get' => array(
                            'order' => 2,
                            'openOrders' => 3,
                            'allOrders' => 10,
                            'account' => 10,
                            'myTrades' => 10,
                            'tradeFee' => 10,
                            'sub-account/list' => 1,
                            'sub-account/apiKey' => 1,
                            'capital/config/getall' => 10,
                            'capital/deposit/hisrec' => 1,
                            'capital/withdraw/history' => 1,
                            'capital/withdraw/address' => 10,
                            'capital/deposit/address' => 10,
                            'capital/transfer' => 1,
                            'capital/transfer/tranId' => 1,
                            'capital/transfer/internal' => 1,
                            'capital/sub-account/universalTransfer' => 1,
                            'capital/convert' => 1,
                            'capital/convert/list' => 1,
                            'margin/loan' => 1,
                            'margin/allOrders' => 1,
                            'margin/myTrades' => 1,
                            'margin/openOrders' => 1,
                            'margin/maxTransferable' => 1,
                            'margin/priceIndex' => 1,
                            'margin/order' => 1,
                            'margin/isolated/account' => 1,
                            'margin/maxBorrowable' => 1,
                            'margin/repay' => 1,
                            'margin/isolated/pair' => 1,
                            'margin/forceLiquidationRec' => 1,
                            'margin/isolatedMarginData' => 1,
                            'margin/isolatedMarginTier' => 1,
                            'rebate/taxQuery' => 1,
                            'rebate/detail' => 1,
                            'rebate/detail/kickback' => 1,
                            'rebate/referCode' => 1,
                            'rebate/affiliate/commission' => 1,
                            'rebate/affiliate/withdraw' => 1,
                            'rebate/affiliate/commission/detail' => 1,
                            'mxDeduct/enable' => 1,
                            'userDataStream' => 1,
                            'selfSymbols' => 1,
                            'asset/internal/transfer/record' => 10,
                        ),
                        'post' => array(
                            'order' => 1,
                            'order/test' => 1,
                            'sub-account/virtualSubAccount' => 1,
                            'sub-account/apiKey' => 1,
                            'sub-account/futures' => 1,
                            'sub-account/margin' => 1,
                            'batchOrders' => 10,
                            'capital/withdraw/apply' => 1,
                            'capital/withdraw' => 1,
                            'capital/transfer' => 1,
                            'capital/transfer/internal' => 1,
                            'capital/deposit/address' => 1,
                            'capital/sub-account/universalTransfer' => 1,
                            'capital/convert' => 10,
                            'mxDeduct/enable' => 1,
                            'userDataStream' => 1,
                        ),
                        'put' => array(
                            'userDataStream' => 1,
                        ),
                        'delete' => array(
                            'order' => 1,
                            'openOrders' => 1,
                            'sub-account/apiKey' => 1,
                            'margin/order' => 1,
                            'margin/openOrders' => 1,
                            'userDataStream' => 1,
                            'capital/withdraw' => 1,
                        ),
                    ),
                ),
                'contract' => array(
                    'public' => array(
                        'get' => array(
                            'ping' => 2,
                            'detail' => 100,
                            'support_currencies' => 2,
                            'depth/{symbol}' => 2,
                            'depth_commits/{symbol}/{limit}' => 2,
                            'index_price/{symbol}' => 2,
                            'fair_price/{symbol}' => 2,
                            'funding_rate/{symbol}' => 2,
                            'kline/{symbol}' => 2,
                            'kline/index_price/{symbol}' => 2,
                            'kline/fair_price/{symbol}' => 2,
                            'deals/{symbol}' => 2,
                            'ticker' => 2,
                            'risk_reverse' => 2,
                            'risk_reverse/history' => 2,
                            'funding_rate/history' => 2,
                        ),
                    ),
                    'private' => array(
                        'get' => array(
                            'account/assets' => 2,
                            'account/asset/{currency}' => 2,
                            'account/transfer_record' => 2,
                            'position/list/history_positions' => 2,
                            'position/open_positions' => 2,
                            'position/funding_records' => 2,
                            'position/position_mode' => 2,
                            'order/list/open_orders/{symbol}' => 2,
                            'order/list/history_orders' => 2,
                            'order/external/{symbol}/{external_oid}' => 2,
                            'order/get/{order_id}' => 2,
                            'order/batch_query' => 8,
                            'order/deal_details/{order_id}' => 2,
                            'order/list/order_deals' => 2,
                            'planorder/list/orders' => 2,
                            'stoporder/list/orders' => 2,
                            'stoporder/order_details/{stop_order_id}' => 2,
                            'account/risk_limit' => 2, // TO_DO => gets max/min position size, allowed sides, leverage, maintenance margin, initial margin, etc...
                            'account/tiered_fee_rate' => 2, // TO_DO => taker/maker fees for account
                            'position/leverage' => 2,
                        ),
                        'post' => array(
                            'position/change_margin' => 2,
                            'position/change_leverage' => 2,
                            'position/change_position_mode' => 2,
                            'order/submit' => 2,
                            'order/submit_batch' => 40,
                            'order/cancel' => 2,
                            'order/cancel_with_external' => 2,
                            'order/cancel_all' => 2,
                            'account/change_risk_level' => 2,
                            'planorder/place' => 2,
                            'planorder/cancel' => 2,
                            'planorder/cancel_all' => 2,
                            'stoporder/cancel' => 2,
                            'stoporder/cancel_all' => 2,
                            'stoporder/change_price' => 2,
                            'stoporder/change_plan_price' => 2,
                        ),
                    ),
                ),
                'spot2' => array(
                    'public' => array(
                        'get' => array(
                            'market/symbols' => 1,
                            'market/coin/list' => 2,
                            'common/timestamp' => 1,
                            'common/ping' => 2,
                            'market/ticker' => 1,
                            'market/depth' => 1,
                            'market/deals' => 1,
                            'market/kline' => 1,
                            'market/api_default_symbols' => 2,
                        ),
                    ),
                    'private' => array(
                        'get' => array(
                            'account/info' => 1,
                            'order/open_orders' => 1,
                            'order/list' => 1,
                            'order/query' => 1,
                            'order/deals' => 1,
                            'order/deal_detail' => 1,
                            'asset/deposit/address/list' => 2,
                            'asset/deposit/list' => 2,
                            'asset/address/list' => 2,
                            'asset/withdraw/list' => 2,
                            'asset/internal/transfer/record' => 10,
                            'account/balance' => 10,
                            'asset/internal/transfer/info' => 10,
                            'market/api_symbols' => 2,
                        ),
                        'post' => array(
                            'order/place' => 1,
                            'order/place_batch' => 1,
                            'order/advanced/place_batch' => 1,
                            'asset/withdraw' => 2,
                            'asset/internal/transfer' => 10,
                        ),
                        'delete' => array(
                            'order/cancel' => 1,
                            'order/cancel_by_symbol' => 1,
                            'asset/withdraw' => 2,
                        ),
                    ),
                ),
                'broker' => array(
                    'private' => array(
                        'get' => array(
                            'sub-account/universalTransfer' => 1,
                            'sub-account/list' => 1,
                            'sub-account/apiKey' => 1,
                            'capital/deposit/subAddress' => 1,
                            'capital/deposit/subHisrec' => 1,
                            'capital/deposit/subHisrec/getall' => 1,
                        ),
                        'post' => array(
                            'sub-account/virtualSubAccount' => 1,
                            'sub-account/apiKey' => 1,
                            'capital/deposit/subAddress' => 1,
                            'capital/withdraw/apply' => 1,
                            'sub-account/universalTransfer' => 1,
                            'sub-account/futures' => 1,
                        ),
                        'delete' => array(
                            'sub-account/apiKey' => 1,
                        ),
                    ),
                ),
            ),
            'precisionMode' => TICK_SIZE,
            'timeframes' => array(
                '1m' => '1m', // spot, swap
                '5m' => '5m', // spot, swap
                '15m' => '15m', // spot, swap
                '30m' => '30m', // spot, swap
                '1h' => '1h', // spot, swap
                '4h' => '4h', // spot, swap
                '8h' => '8h', // swap
                '1d' => '1d', // spot, swap
                '1w' => '1w', // swap
                '1M' => '1M', // spot, swap
            ),
            'fees' => array(
                'trading' => array(
                    'tierBased' => false,
                    'percentage' => true,
                    'maker' => $this->parse_number('0.002'), // maker / taker
                    'taker' => $this->parse_number('0.002'),
                ),
            ),
            'options' => array(
                'adjustForTimeDifference' => false,
                'timeDifference' => 0,
                'unavailableContracts' => array(
                    'BTC/USDT:USDT' => true,
                    'LTC/USDT:USDT' => true,
                    'ETH/USDT:USDT' => true,
                ),
                'fetchMarkets' => array(
                    'types' => array(
                        'spot' => true,
                        'swap' => array(
                            'linear' => true,
                            'inverse' => false,
                        ),
                    ),
                ),
                'useCcxtTradeId' => true,
                'timeframes' => array(
                    'spot' => array(
                        '1m' => '1m',
                        '5m' => '5m',
                        '15m' => '15m',
                        '30m' => '30m',
                        '1h' => '60m',
                        '4h' => '4h',
                        '1d' => '1d',
                        '1w' => '1W',
                        '1M' => '1M',
                    ),
                    'swap' => array(
                        '1m' => 'Min1',
                        '5m' => 'Min5',
                        '15m' => 'Min15',
                        '30m' => 'Min30',
                        '1h' => 'Min60',
                        '4h' => 'Hour4',
                        '8h' => 'Hour8',
                        '1d' => 'Day1',
                        '1w' => 'Week1',
                        '1M' => 'Month1',
                    ),
                ),
                'defaultType' => 'spot', // spot, swap
                'defaultNetwork' => 'ETH',
                'defaultNetworks' => array(
                    'ETH' => 'ETH',
                    'USDT' => 'ERC20',
                    'USDC' => 'ERC20',
                    'BTC' => 'BTC',
                    'LTC' => 'LTC',
                ),
                'networks' => array(
                    'ZKSYNC' => 'ZKSYNCERA',
                    'TRC20' => 'TRX',
                    'TON' => 'TONCOIN',
                    'ARBITRUM' => 'ARB',
                    'STX' => 'STACKS',
                    'LUNC' => 'LUNA',
                    'STARK' => 'STARKNET',
                    'APT' => 'APTOS',
                    'PEAQ' => 'PEAQEVM',
                    'AVAXC' => 'AVAX_CCHAIN',
                    'ERC20' => 'ETH',
                    'ACA' => 'ACALA',
                    'BEP20' => 'BSC',
                    'OPTIMISM' => 'OP',
                    // 'ADA' => 'Cardano(ADA)',
                    // 'AE' => 'AE',
                    // 'ALGO' => 'Algorand(ALGO)',
                    // 'ALPH' => 'Alephium(ALPH)',
                    // 'ARB' => 'Arbitrum One(ARB)',
                    // 'ARBONE' => 'ArbitrumOne(ARB)',
                    'ASTR' => 'ASTAR', // ASTAREVM is different
                    // 'ATOM' => 'Cosmos(ATOM)',
                    // 'AVAXC' => 'Avalanche C Chain(AVAX CCHAIN)',
                    // 'AVAXX' => 'Avalanche X Chain(AVAX XCHAIN)',
                    // 'AZERO' => 'Aleph Zero(AZERO)',
                    // 'BCH' => 'Bitcoin Cash(BCH)',
                    // 'BNCDOT' => 'BNCPOLKA',
                    // 'BSV' => 'Bitcoin SV(BSV)',
                    // 'BTC' => 'Bitcoin(BTC)',
                    'BTM' => 'BTM2',
                    // 'CHZ' => 'Chiliz Legacy Chain(CHZ)',
                    // 'CHZ2' => 'Chiliz Chain(CHZ2)',
                    // 'CLORE' => 'Clore.ai(CLORE)',
                    'CRC20' => 'CRONOS',
                    // 'DC' => 'Dogechain(DC)',
                    // 'DNX' => 'Dynex(DNX)',
                    // 'DOGE' => 'Dogecoin(DOGE)',
                    // 'DOT' => 'Polkadot(DOT)',
                    'DOT' => 'DOTASSETHUB',
                    // 'DYM' => 'Dymension(DYM)',
                    'ETHF' => 'ETF',
                    'HRC20' => 'HECO',
                    // 'KLAY' => 'Klaytn(KLAY)',
                    'OASIS' => 'ROSE',
                    'OKC' => 'OKT',
                    'RSK' => 'RBTC',
                    // 'RVN' => 'Ravencoin(RVN)',
                    // 'SATOX' => 'Satoxcoin(SATOX)',
                    // 'SC' => 'SC',
                    // 'SCRT' => 'SCRT',
                    // 'SDN' => 'SDN',
                    // 'SGB' => 'SGB',
                    // 'SOL' => 'Solana(SOL)',
                    // 'STAR' => 'STAR',
                    // 'STARK' => 'Starknet(STARK)',
                    // 'STEEM' => 'STEEM',
                    // 'SYS' => 'SYS',
                    // 'TAO' => 'Bittensor(TAO)',
                    // 'TIA' => 'Celestia(TIA)',
                    // 'TOMO' => 'TOMO',
                    // 'TON' => 'Toncoin(TON)',
                    // 'TRC10' => 'TRC10',
                    // 'TRC20' => 'Tron(TRC20)',
                    // 'UGAS' => 'UGAS(Ultrain)',
                    // 'VET' => 'VeChain(VET)',
                    // 'VEX' => 'Vexanium(VEX)',
                    // 'VSYS' => 'VSYS',
                    // 'WAVES' => 'WAVES',
                    // 'WAX' => 'WAX',
                    // 'WEMIX' => 'WEMIX',
                    // 'XCH' => 'Chia(XCH)',
                    // 'XDC' => 'XDC',
                    // 'XEC' => 'XEC',
                    // 'XLM' => 'Stellar(XLM)',
                    // 'XMR' => 'Monero(XMR)',
                    // 'XNA' => 'Neurai(XNA)',
                    // 'XPR' => 'XPR Network',
                    // 'XRD' => 'XRD',
                    // 'XRP' => 'Ripple(XRP)',
                    // 'XTZ' => 'XTZ',
                    // 'XVG' => 'XVG',
                    // 'XYM' => 'XYM',
                    // 'ZEC' => 'ZEC',
                    // 'ZEN' => 'ZEN',
                    // 'ZIL' => 'Zilliqa(ZIL)',
                    // 'ZTG' => 'ZTG',
                    // todo => uncomment below after concensus
                    // 'ALAYA' => 'ATP',
                    // 'ANDUSCHAIN' => 'DEB',
                    // 'ASSETMANTLE' => 'MNTL',
                    // 'AXE' => 'AXE',
                    // 'BITCOINHD' => 'BHD',
                    // 'BITCOINVAULT' => 'BTCV',
                    // 'BITKUB' => 'KUB',
                    // 'BITSHARES_OLD' => 'BTS',
                    // 'BITSHARES' => 'NBS',
                    // 'BYTZ' => 'BYTZ',
                    // 'CANTO' => 'CANTO', // CANTOEVM
                    // 'CENNZ' => 'CENNZ',
                    // 'CHAINX' => 'PCX',
                    // 'CONCODRIUM' => 'CCD',
                    // 'CONTENTVALUENETWORK' => 'CVNT',
                    // 'CORTEX' => 'CTXC',
                    // 'CYPHERIUM' => 'CPH',
                    // 'DANGNN' => 'DGC',
                    // 'DARWINIASMARTCHAIN' => 'Darwinia Smart Chain',
                    // 'DHEALTH' => 'DHP',
                    // 'DOGECOIN' => array( 'DOGE', 'DOGECHAIN' ), // todo after unification
                    // 'DRAC' => 'DRAC',
                    // 'DRAKEN' => 'DRK',
                    // 'ECOCHAIN' => 'ECOC',
                    // 'ELECTRAPROTOCOL' => 'XEP',
                    // 'EMERALD' => 'EMERALD', // sits on top of OASIS
                    // 'EVMOS' => 'EVMOS', // EVMOSETH is different
                    // 'EXOSAMA' => 'SAMA',
                    // 'FIBOS' => 'FO',
                    // 'FILECASH' => 'FIC',
                    // 'FIRMACHAIN' => 'FCT',
                    // 'FIRO' => 'XZC',
                    // 'FNCY' => 'FNCY',
                    // 'FRUITS' => 'FRTS',
                    // 'GLEEC' => 'GLEEC',
                    // 'GXCHAIN' => 'GXC',
                    // 'HANDSHAKE' => 'HNS',
                    // 'HPB' => 'HPB',
                    // 'HSHARE' => 'HC',
                    // 'HUAHUA' => 'HUAHUA',
                    // 'HUPAYX' => 'HPX',
                    // 'INDEXCHAIN' => 'IDX',
                    // 'INTCHAIN' => 'INT',
                    // 'INTEGRITEE' => 'TEER',
                    // 'INTERLAY' => 'INTR',
                    // 'IOEX' => 'IOEX',
                    // 'JUNO' => 'JUNO',
                    // 'KASPA' => 'KASPA',
                    // 'KEKCHAIN' => 'KEKCHAIN',
                    // 'KINTSUGI' => 'KINT',
                    // 'KOINOS' => 'KOINOS',
                    // 'KONSTELLATION' => 'DARC',
                    // 'KUJIRA' => 'KUJI',
                    // 'KULUPU' => 'KLP',
                    // 'LBRY' => 'LBC',
                    // 'LEDGIS' => 'LED',
                    // 'LIGHTNINGBITCOIN' => 'LBTC',
                    // 'LINE' => 'LINE',
                    // 'MDNA' => 'DNA',
                    // 'MDUKEY' => 'MDU',
                    // 'METAMUI' => 'MMUI',
                    // 'METAVERSE_ETP' => 'ETP',
                    // 'METER' => 'MTRG',
                    // 'MEVERSE' => 'MEVerse',
                    // 'NEWTON' => 'NEW',
                    // 'NODLE' => 'NODLE',
                    // 'ORIGYN' => 'OGY',
                    // 'PAC' => 'PAC',
                    // 'PASTEL' => 'PSL',
                    // 'PHALA' => 'Khala',
                    // 'PLEX' => 'PLEX',
                    // 'PMG' => 'PMG',
                    // 'POINT' => 'POINT', // POINTEVM is different
                    // 'PROOFOFMEMES' => 'POM',
                    // 'PROXIMAX' => 'XPX',
                    // 'RCHAIN' => 'REV',
                    // 'REBUS' => 'REBUS', // REBUSEVM is different
                    // 'RIZON' => 'ATOLO',
                    // 'SENTINEL' => 'DVPN',
                    // 'SERO' => 'SERO',
                    // 'TECHPAY' => 'TPC',
                    // 'TELOSCOIN' => 'TLOS', // todo
                    // 'TERRA' => 'LUNA2',
                    // 'TERRACLASSIC' => 'LUNC',
                    // 'TLOS' => 'TELOS', // todo
                    // 'TOMAINFO' => 'TON',
                    // 'TONGTONG' => 'TTC',
                    // 'TURTLECOIN' => 'TRTL',
                    // 'ULORD' => 'UT',
                    // 'ULTRAIN' => 'UGAS',
                    // 'UMEE' => 'UMEE',
                    // 'VDIMENSION' => 'VOLLAR',
                    // 'VEXANIUM' => 'VEX',
                    // 'VNT' => 'VNT',
                    // 'WAYKICHAIN' => 'WICC',
                    // 'WHITECOIN' => 'XWC',
                    // 'WITNET' => 'WIT',
                    // 'XDAI' => 'XDAI',
                    // 'XX' => 'XX',
                    // 'YAS' => 'YAS',
                    // 'ZENITH' => 'ZENITH',
                    // 'ZKSYNC' => 'ZKSYNC',
                    // // 'BAJUN' => '',
                    // OKB <> OKT (for usdt it's exception) for OKC, PMEER, FLARE, STRD, ZEL, FUND, "NONE", CRING, FREETON, QTZ  (probably unique network is meant), HT, BSC(RACAV1), BSC(RACAV2), AMBROSUS, BAJUN, NOM. their individual info is at https://www.mexc.com/api/platform/asset/spot/{COINNAME}
                ),
                'networksById' => array(
                    'BNB Smart Chain(BEP20-RACAV1)' => 'BSC',
                    'BNB Smart Chain(BEP20-RACAV2)' => 'BSC',
                    'BNB Smart Chain(BEP20)' => 'BSC',
                    'Ethereum(ERC20)' => 'ERC20',
                    // TODO => uncomment below after deciding unified name
                    // 'PEPE COIN BSC':
                    // 'SMART BLOCKCHAIN':
                    // 'f(x)Core':
                    // 'Syscoin Rollux':
                    // 'Syscoin UTXO':
                    // 'zkSync Era':
                    // 'zkSync Lite':
                    // 'Darwinia Smart Chain':
                    // 'Arbitrum One(ARB-Bridged)':
                    // 'Optimism(OP-Bridged)':
                    // 'Polygon(MATIC-Bridged)':
                ),
                'recvWindow' => 5 * 1000, // 5 sec, default
                'maxTimeTillEnd' => 90 * 86400 * 1000 - 1, // 90 days
                'broker' => 'CCXT',
            ),
            'features' => array(
                'default' => array(
                    'sandbox' => false,
                    'createOrder' => array(
                        'marginMode' => true,
                        'triggerPrice' => false,
                        'triggerDirection' => false,
                        'triggerPriceType' => array(
                            'last' => false,
                            'mark' => false,
                            'index' => false,
                        ),
                        'stopLossPrice' => false, // todo
                        'takeProfitPrice' => false,
                        'attachedStopLossTakeProfit' => null,
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => true,
                            'PO' => true,
                            'GTD' => false,
                        ),
                        'hedged' => true, // todo implement
                        'trailing' => false,
                        'leverage' => true, // todo implement
                        'marketBuyByCost' => true,
                        'marketBuyRequiresPrice' => false,
                        'selfTradePrevention' => false,
                        'iceberg' => false,
                    ),
                    'createOrders' => array(
                        'max' => 20,
                    ),
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'daysBack' => 30,
                        'untilDays' => null,
                        'symbolRequired' => true,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => true,
                        'limit' => null,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOrders' => array(
                        'marginMode' => true,
                        'limit' => 1000,
                        'daysBack' => 7,
                        'untilDays' => 7,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchClosedOrders' => array(
                        'marginMode' => true,
                        'limit' => 1000,
                        'daysBack' => 7,
                        'daysBackCanceled' => 7,
                        'untilDays' => 7,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => true,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 1000,
                    ),
                ),
                'spot' => array(
                    'extends' => 'default',
                    'fetchCurrencies' => array(
                        'private' => true,
                    ),
                ),
                'forDerivs' => array(
                    'extends' => 'default',
                    'createOrder' => array(
                        'triggerPrice' => true,
                        'triggerPriceType' => array(
                            'last' => true,
                            'mark' => true,
                            'index' => true,
                        ),
                        'triggerDirection' => true, // todo
                        'stopLossPrice' => false, // todo
                        'takeProfitPrice' => false, // todo
                        'hedged' => true,
                        'leverage' => true, // todo
                        'marketBuyByCost' => false,
                    ),
                    'createOrders' => null, // todo => needs implementation https://mexcdevelop.github.io/apidocs/contract_v1_en/#order-under-maintenance:~:text=Order%20the%20contract%20in%20batch
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'daysBack' => 90,
                        'untilDays' => 90,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'trigger' => true,
                        'trailing' => false,
                    ),
                    'fetchOrders' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'daysBack' => 90,
                        'untilDays' => 90,
                        'trigger' => true,
                        'trailing' => false,
                    ),
                    'fetchClosedOrders' => array(
                        'marginMode' => false,
                        'limit' => 100,
                        'daysBack' => 90,
                        'daysBackCanceled' => null,
                        'untilDays' => 90,
                        'trigger' => true,
                        'trailing' => false,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 2000,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'forDerivs',
                    ),
                    'inverse' => array(
                        'extends' => 'forDerivs',
                    ),
                ),
                'future' => array(
                    'linear' => null,
                    'inverse' => null,
                ),
            ),
            'commonCurrencies' => array(
                'BEYONDPROTOCOL' => 'BEYOND',
                'BIFI' => 'BIFIF',
                'BYN' => 'BEYONDFI',
                'COFI' => 'COFIX', // conflict with CoinFi
                'DFI' => 'DFISTARTER',
                'DFT' => 'DFUTURE',
                'DRK' => 'DRK',
                'EGC' => 'EGORASCREDIT',
                'FLUX1' => 'FLUX', // switched places
                'FLUX' => 'FLUX1', // switched places
                'FREE' => 'FREEROSSDAO', // conflict with FREE Coin
                'GAS' => 'GASDAO',
                'GASNEO' => 'GAS',
                'GMT' => 'GMTTOKEN', // Conflict with GMT (STEPN)
                'STEPN' => 'GMT', // Conflict with GMT Token
                'HERO' => 'STEPHERO', // conflict with Metahero
                'MIMO' => 'MIMOSA',
                'PROS' => 'PROSFINANCE', // conflict with Prosper
                'SIN' => 'SINCITYTOKEN',
                'SOUL' => 'SOULSWAP',
                'XBT' => 'XBT', // restore original mapping
            ),
            'exceptions' => array(
                'exact' => array(
                    // until mexc migrates fully to v3, it might be worth to note the version & market aside errors, not easily remove obsolete version's exceptions in future
                    '-1128' => '\\ccxt\\BadRequest',
                    '-2011' => '\\ccxt\\BadRequest',
                    '-1121' => '\\ccxt\\BadSymbol',
                    '10101' => '\\ccxt\\InsufficientFunds', // array("msg":"资金不足","code":10101)
                    '2009' => '\\ccxt\\InvalidOrder', // array("success":false,"code":2009,"message":"Position is not exists or closed.")
                    '2011' => '\\ccxt\\BadRequest',
                    '30004' => '\\ccxt\\InsufficientFunds',
                    '33333' => '\\ccxt\\BadRequest', // array("msg":"Not support transfer","code":33333)
                    '44444' => '\\ccxt\\BadRequest',
                    '1002' => '\\ccxt\\InvalidOrder',
                    '30019' => '\\ccxt\\BadRequest',
                    '30005' => '\\ccxt\\InvalidOrder',
                    '2003' => '\\ccxt\\InvalidOrder',
                    '2005' => '\\ccxt\\InsufficientFunds',
                    '400' => '\\ccxt\\BadRequest', // array("msg":"The start time cannot be earlier than 90 days","code":400)
                    // '500' => '\\ccxt\\OnMaintenance', // array("code" => 500,"message" => "Under maintenance, please try again later","announcement" => "https://www.mexc.com/support/articles/17827791510263")
                    '600' => '\\ccxt\\BadRequest',
                    '70011' => '\\ccxt\\PermissionDenied', // array("code":70011,"msg":"Pair user ban trade apikey.")
                    '88004' => '\\ccxt\\InsufficientFunds', // array("msg":"超出最大可借，最大可借币为:18.09833211","code":88004)
                    '88009' => '\\ccxt\\ExchangeError', // v3 array("msg":"Loan record does not exist","code":88009)
                    '88013' => '\\ccxt\\InvalidOrder', // array("msg":"最小交易额不能小于：5USDT","code":88013)
                    '88015' => '\\ccxt\\InsufficientFunds', // array("msg":"持仓不足","code":88015)
                    '700003' => '\\ccxt\\InvalidNonce', // array("code":700003,"msg":"Timestamp for this request is outside of the recvWindow.")
                    '26' => '\\ccxt\\ExchangeError', // operation not allowed
                    '602' => '\\ccxt\\AuthenticationError', // Signature verification failed
                    '10001' => '\\ccxt\\AuthenticationError', // user does not exist
                    '10007' => '\\ccxt\\BadSymbol', // array("code":10007,"msg":"bad symbol")
                    '10015' => '\\ccxt\\BadRequest', // user id cannot be null
                    '10072' => '\\ccxt\\BadRequest', // invalid access key
                    '10073' => '\\ccxt\\BadRequest', // invalid Request-Time
                    '10095' => '\\ccxt\\InvalidOrder', // amount cannot be null
                    '10096' => '\\ccxt\\InvalidOrder', // amount decimal places is too long
                    '10097' => '\\ccxt\\InvalidOrder', // amount is error
                    '10098' => '\\ccxt\\InvalidOrder', // risk control system detected abnormal
                    '10099' => '\\ccxt\\BadRequest', // user sub account does not open
                    '10100' => '\\ccxt\\BadRequest', // this currency transfer is not supported
                    '10102' => '\\ccxt\\InvalidOrder', // amount cannot be zero or negative
                    '10103' => '\\ccxt\\ExchangeError', // this account transfer is not supported
                    '10200' => '\\ccxt\\BadRequest', // transfer operation processing
                    '10201' => '\\ccxt\\BadRequest', // transfer in failed
                    '10202' => '\\ccxt\\BadRequest', // transfer out failed
                    '10206' => '\\ccxt\\BadRequest', // transfer is disabled
                    '10211' => '\\ccxt\\BadRequest', // transfer is forbidden
                    '10212' => '\\ccxt\\BadRequest', // This withdrawal address is not on the commonly used address list or has been invalidated
                    '10216' => '\\ccxt\\ExchangeError', // no address available. Please try again later
                    '10219' => '\\ccxt\\ExchangeError', // asset flow writing failed please try again
                    '10222' => '\\ccxt\\BadRequest', // currency cannot be null
                    '10232' => '\\ccxt\\BadRequest', // currency does not exist
                    '10259' => '\\ccxt\\ExchangeError', // Intermediate account does not configured in redisredis
                    '10265' => '\\ccxt\\ExchangeError', // Due to risk control, withdrawal is unavailable, please try again later
                    '10268' => '\\ccxt\\BadRequest', // remark length is too long
                    '20001' => '\\ccxt\\ExchangeError', // subsystem is not supported
                    '20002' => '\\ccxt\\ExchangeError', // Internal system error please contact support
                    '22222' => '\\ccxt\\BadRequest', // record does not exist
                    '30000' => '\\ccxt\\ExchangeError', // suspended transaction for the symbol
                    '30001' => '\\ccxt\\InvalidOrder', // The current transaction direction is not allowed to place an order
                    '30002' => '\\ccxt\\InvalidOrder', // The minimum transaction volume cannot be less than :
                    '30003' => '\\ccxt\\InvalidOrder', // The maximum transaction volume cannot be greater than :
                    '30010' => '\\ccxt\\InvalidOrder', // no valid trade price
                    '30014' => '\\ccxt\\InvalidOrder', // invalid symbol
                    '30016' => '\\ccxt\\InvalidOrder', // trading disabled
                    '30018' => '\\ccxt\\AccountSuspended', // array("msg":"账号暂时不能下单，请联系客服","code":30018)
                    '30020' => '\\ccxt\\AuthenticationError', // no permission for the symbol
                    '30021' => '\\ccxt\\BadRequest', // invalid symbol
                    '30025' => '\\ccxt\\InvalidOrder', // no exist opponent order
                    '30026' => '\\ccxt\\BadRequest', // invalid order ids
                    '30027' => '\\ccxt\\InvalidOrder', // The currency has reached the maximum position limit, the buying is suspended
                    '30028' => '\\ccxt\\InvalidOrder', // The currency triggered the platform risk control, the selling is suspended
                    '30029' => '\\ccxt\\InvalidOrder', // Cannot exceed the maximum order limit
                    '30032' => '\\ccxt\\InvalidOrder', // Cannot exceed the maximum position
                    '30041' => '\\ccxt\\InvalidOrder', // current order type can not place order
                    '60005' => '\\ccxt\\ExchangeError', // your account is abnormal
                    '700001' => '\\ccxt\\AuthenticationError', // array("code":700002,"msg":"Signature for this request is not valid.") // same message for expired API keys
                    '700002' => '\\ccxt\\AuthenticationError', // Signature for this request is not valid // or the API secret is incorrect
                    '700004' => '\\ccxt\\BadRequest', // Param 'origClientOrderId' or 'orderId' must be sent, but both were empty/null
                    '700005' => '\\ccxt\\InvalidNonce', // recvWindow must less than 60000
                    '700006' => '\\ccxt\\BadRequest', // IP non white list
                    '700007' => '\\ccxt\\AuthenticationError', // No permission to access the endpoint
                    '700008' => '\\ccxt\\BadRequest', // Illegal characters found in parameter
                    '700013' => '\\ccxt\\AuthenticationError', // Invalid Content-Type v3
                    '730001' => '\\ccxt\\BadRequest', // Pair not found
                    '730002' => '\\ccxt\\BadRequest', // Your input param is invalid
                    '730000' => '\\ccxt\\ExchangeError', // Request failed, please contact the customer service
                    '730003' => '\\ccxt\\ExchangeError', // Unsupported operation, please contact the customer service
                    '730100' => '\\ccxt\\ExchangeError', // Unusual user status
                    '730600' => '\\ccxt\\BadRequest', // Sub-account Name cannot be null
                    '730601' => '\\ccxt\\BadRequest', // Sub-account Name must be a combination of 8-32 letters and numbers
                    '730602' => '\\ccxt\\BadRequest', // Sub-account remarks cannot be null
                    '730700' => '\\ccxt\\BadRequest', // API KEY remarks cannot be null
                    '730701' => '\\ccxt\\BadRequest', // API KEY permission cannot be null
                    '730702' => '\\ccxt\\BadRequest', // API KEY permission does not exist
                    '730703' => '\\ccxt\\BadRequest', // The IP information is incorrect, and a maximum of 10 IPs are allowed to be bound only
                    '730704' => '\\ccxt\\BadRequest', // The bound IP format is incorrect, please refill
                    '730705' => '\\ccxt\\BadRequest', // At most 30 groups of Api Keys are allowed to be created only
                    '730706' => '\\ccxt\\BadRequest', // API KEY information does not exist
                    '730707' => '\\ccxt\\BadRequest', // accessKey cannot be null
                    '730101' => '\\ccxt\\BadRequest', // The user Name already exists
                    '140001' => '\\ccxt\\BadRequest', // sub account does not exist
                    '140002' => '\\ccxt\\AuthenticationError', // sub account is forbidden
                ),
                'broad' => array(
                    'Order quantity error, please try to modify.' => '\\ccxt\\BadRequest', // code:2011
                    'Combination of optional parameters invalid' => '\\ccxt\\BadRequest', // code:-2011
                    'api market order is disabled' => '\\ccxt\\BadRequest', //
                    'Contract not allow place order!' => '\\ccxt\\InvalidOrder', // code:1002
                    'Oversold' => '\\ccxt\\InsufficientFunds', // code:30005
                    'Insufficient position' => '\\ccxt\\InsufficientFunds', // code:30004
                    'Insufficient balance!' => '\\ccxt\\InsufficientFunds', // code:2005
                    'Bid price is great than max allow price' => '\\ccxt\\InvalidOrder', // code:2003
                    'Invalid symbol.' => '\\ccxt\\BadSymbol', // code:-1121
                    'Param error!' => '\\ccxt\\BadRequest', // code:600
                    'maintenance' => '\\ccxt\\OnMaintenance', // array("code" => 500,"message" => "Under maintenance, please try again later","announcement" => "https://www.mexc.com/support/articles/17827791510263")
                ),
            ),
        ));
    }

    public function fetch_status($params = array ()) {
        return Async\async(function () use ($params) {
            /**
             * the latest known information on the availability of the exchange API
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#test-connectivity
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-server-time
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=exchange-$status-structure $status structure~
             */
            list($marketType, $query) = $this->handle_market_type_and_params('fetchStatus', null, $params);
            $response = null;
            $status = null;
            $updated = null;
            if ($marketType === 'spot') {
                $response = Async\await($this->spotPublicGetPing ($query));
                //
                //     array()
                //
                $keys = is_array($response) ? array_keys($response) : array();
                $length = count($keys);
                $status = $length ? $this->json($response) : 'ok';
            } elseif ($marketType === 'swap') {
                $response = Async\await($this->contractPublicGetPing ($query));
                //
                //     array("success":true,"code":"0","data":"1648124374985")
                //
                $status = $this->safe_value($response, 'success') ? 'ok' : $this->json($response);
                $updated = $this->safe_integer($response, 'data');
            }
            return array(
                'status' => $status,
                'updated' => $updated,
                'url' => null,
                'eta' => null,
                'info' => $response,
            );
        }) ();
    }

    public function fetch_time($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches the current integer timestamp in milliseconds from the exchange server
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#check-server-time
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-server-time
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {int} the current integer timestamp in milliseconds from the exchange server
             */
            list($marketType, $query) = $this->handle_market_type_and_params('fetchTime', null, $params);
            $response = null;
            if ($marketType === 'spot') {
                $response = Async\await($this->spotPublicGetTime ($query));
                //
                //     array("serverTime" => "1647519277579")
                //
                return $this->safe_integer($response, 'serverTime');
            } elseif ($marketType === 'swap') {
                $response = Async\await($this->contractPublicGetPing ($query));
                //
                //     array("success":true,"code":"0","data":"1648124374985")
                //
                return $this->safe_integer($response, 'data');
            }
            return null;
        }) ();
    }

    public function fetch_currencies($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches all available currencies on an exchange
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#query-the-$currency-information
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an associative dictionary of currencies
             */
            // this endpoint requires authentication
            // while fetchCurrencies is a public API method by design
            // therefore we check the keys here
            // and fallback to generating the currencies from the markets
            if (!$this->check_required_credentials(false)) {
                return array();
            }
            $response = Async\await($this->spotPrivateGetCapitalConfigGetall ($params));
            //
            // {
            //     "coin" => "QANX",
            //     "name" => "QANplatform",
            //     "networkList" => array(
            //       array(
            //         "coin" => "QANX",
            //         "depositDesc" => null,
            //         "depositEnable" => true,
            //         "minConfirm" => "0",
            //         "name" => "QANplatform",
            //         "network" => "BEP20(BSC)",
            //         "withdrawEnable" => false,
            //         "withdrawFee" => "42.000000000000000000",
            //         "withdrawIntegerMultiple" => null,
            //         "withdrawMax" => "24000000.000000000000000000",
            //         "withdrawMin" => "20.000000000000000000",
            //         "sameAddress" => false,
            //         "contract" => "0xAAA7A10a8ee237ea61E8AC46C50A8Db8bCC1baaa"
            //       ),
            //       {
            //         "coin" => "QANX",
            //         "depositDesc" => null,
            //         "depositEnable" => true,
            //         "minConfirm" => "0",
            //         "name" => "QANplatform",
            //         "network" => "ERC20",
            //         "withdrawEnable" => true,
            //         "withdrawFee" => "2732.000000000000000000",
            //         "withdrawIntegerMultiple" => null,
            //         "withdrawMax" => "24000000.000000000000000000",
            //         "withdrawMin" => "240.000000000000000000",
            //         "sameAddress" => false,
            //         "contract" => "0xAAA7A10a8ee237ea61E8AC46C50A8Db8bCC1baaa"
            //       }
            //     )
            //   }
            //
            $result = array();
            for ($i = 0; $i < count($response); $i++) {
                $currency = $response[$i];
                $id = $this->safe_string($currency, 'coin');
                $code = $this->safe_currency_code($id);
                $networks = array();
                $chains = $this->safe_value($currency, 'networkList', array());
                for ($j = 0; $j < count($chains); $j++) {
                    $chain = $chains[$j];
                    $networkId = $this->safe_string_2($chain, 'netWork', 'network');
                    $network = $this->network_id_to_code($networkId);
                    $networks[$network] = array(
                        'info' => $chain,
                        'id' => $networkId,
                        'network' => $network,
                        'active' => null,
                        'deposit' => $this->safe_bool($chain, 'depositEnable', false),
                        'withdraw' => $this->safe_bool($chain, 'withdrawEnable', false),
                        'fee' => $this->safe_number($chain, 'withdrawFee'),
                        'precision' => null,
                        'limits' => array(
                            'withdraw' => array(
                                'min' => $this->safe_string($chain, 'withdrawMin'),
                                'max' => $this->safe_string($chain, 'withdrawMax'),
                            ),
                        ),
                        'contract' => $this->safe_string($chain, 'contract'),
                    );
                }
                $result[$code] = $this->safe_currency_structure(array(
                    'info' => $currency,
                    'id' => $id,
                    'code' => $code,
                    'name' => $this->safe_string($currency, 'name'),
                    'active' => null,
                    'deposit' => null,
                    'withdraw' => null,
                    'fee' => null,
                    'precision' => null,
                    'limits' => array(
                        'amount' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                    'type' => 'crypto',
                    'networks' => $networks,
                ));
            }
            return $result;
        }) ();
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * retrieves data on all markets for mexc
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#exchange-information
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-contract-information
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing market data
             */
            if ($this->options['adjustForTimeDifference']) {
                Async\await($this->load_time_difference());
            }
            $spotMarketPromise = $this->fetch_spot_markets($params);
            $swapMarketPromise = $this->fetch_swap_markets($params);
            list($spotMarket, $swapMarket) = Async\await(Promise\all(array( $spotMarketPromise, $swapMarketPromise )));
            return $this->array_concat($spotMarket, $swapMarket);
        }) ();
    }

    public function fetch_spot_markets($params = array ()) {
        return Async\async(function () use ($params) {
            /**
             * @ignore
             * retrieves $data on all spot markets for mexc
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#exchange-information
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing $market $data
             */
            $response = Async\await($this->spotPublicGetExchangeInfo ($params));
            //
            //     {
            //         "timezone" => "CST",
            //         "serverTime" => 1647521860402,
            //         "rateLimits" => array(),
            //         "exchangeFilters" => array(),
            //         "symbols" => array(
            //           array(
            //                "symbol" => "OGNUSDT",
            //                "status" => "1",
            //                "baseAsset" => "OGN",
            //                "baseAssetPrecision" => "2",
            //                "quoteAsset" => "USDT",
            //                "quoteAssetPrecision" => "4",
            //                "orderTypes" => array(
            //                    "LIMIT",
            //                    "LIMIT_MAKER"
            //                ),
            //                "baseCommissionPrecision" => "2",
            //                "quoteCommissionPrecision" => "4",
            //                "quoteOrderQtyMarketAllowed" => false,
            //                "isSpotTradingAllowed" => true,
            //                "isMarginTradingAllowed" => true,
            //                "permissions" => array(
            //                    "SPOT",
            //                    "MARGIN"
            //                ),
            //                "filters" => array(),
            //                "baseSizePrecision" => "0.01", // this turned out to be a minimum $base amount for order
            //                "maxQuoteAmount" => "5000000",
            //                "makerCommission" => "0.002",
            //                "takerCommission" => "0.002"
            //                "quoteAmountPrecision" => "5", // this turned out to be a minimum cost amount for order
            //                "quotePrecision" => "4", // deprecated in favor of 'quoteAssetPrecision' ( https://dev.binance.vision/t/what-is-the-difference-between-quoteprecision-and-quoteassetprecision/4333 )
            //                // note, "icebergAllowed" & "ocoAllowed" fields were recently removed
            //            ),
            //         )
            //     }
            //
            // Notes:
            // - 'quoteAssetPrecision' & 'baseAssetPrecision' are not currency's real blockchain precision (to view currency's actual individual precision, refer to fetchCurrencies() method).
            //
            $data = $this->safe_value($response, 'symbols', array());
            $result = array();
            for ($i = 0; $i < count($data); $i++) {
                $market = $data[$i];
                $id = $this->safe_string($market, 'symbol');
                $baseId = $this->safe_string($market, 'baseAsset');
                $quoteId = $this->safe_string($market, 'quoteAsset');
                $base = $this->safe_currency_code($baseId);
                $quote = $this->safe_currency_code($quoteId);
                $status = $this->safe_string($market, 'status');
                $isSpotTradingAllowed = $this->safe_value($market, 'isSpotTradingAllowed');
                $active = false;
                if (($status === '1') && ($isSpotTradingAllowed)) {
                    $active = true;
                }
                $isMarginTradingAllowed = $this->safe_value($market, 'isMarginTradingAllowed');
                $makerCommission = $this->safe_number($market, 'makerCommission');
                $takerCommission = $this->safe_number($market, 'takerCommission');
                $maxQuoteAmount = $this->safe_number($market, 'maxQuoteAmount');
                $result[] = array(
                    'id' => $id,
                    'symbol' => $base . '/' . $quote,
                    'base' => $base,
                    'quote' => $quote,
                    'settle' => null,
                    'baseId' => $baseId,
                    'quoteId' => $quoteId,
                    'settleId' => null,
                    'type' => 'spot',
                    'spot' => true,
                    'margin' => $isMarginTradingAllowed,
                    'swap' => false,
                    'future' => false,
                    'option' => false,
                    'active' => $active,
                    'contract' => false,
                    'linear' => null,
                    'inverse' => null,
                    'taker' => $takerCommission,
                    'maker' => $makerCommission,
                    'contractSize' => null,
                    'expiry' => null,
                    'expiryDatetime' => null,
                    'strike' => null,
                    'optionType' => null,
                    'precision' => array(
                        'amount' => $this->parse_number($this->parse_precision($this->safe_string($market, 'baseAssetPrecision'))),
                        'price' => $this->parse_number($this->parse_precision($this->safe_string($market, 'quoteAssetPrecision'))),
                    ),
                    'limits' => array(
                        'leverage' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'amount' => array(
                            'min' => $this->safe_number($market, 'baseSizePrecision'),
                            'max' => null,
                        ),
                        'price' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'cost' => array(
                            'min' => $this->safe_number($market, 'quoteAmountPrecision'),
                            'max' => $maxQuoteAmount,
                        ),
                    ),
                    'created' => null,
                    'info' => $market,
                );
            }
            return $result;
        }) ();
    }

    public function fetch_swap_markets($params = array ()) {
        return Async\async(function () use ($params) {
            /**
             * @ignore
             * retrieves $data on all swap markets for mexc
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-contract-information
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing $market $data
             */
            $currentRl = $this->rateLimit;
            $this->set_property($this, 'rateLimit', 10); // see comment => https://github.com/ccxt/ccxt/pull/23698
            $response = Async\await($this->contractPublicGetDetail ($params));
            $this->set_property($this, 'rateLimit', $currentRl);
            //
            //     {
            //         "success":true,
            //         "code":0,
            //         "data":[
            //             array(
            //                 "symbol":"BTC_USDT",
            //                 "displayName":"BTC_USDT永续",
            //                 "displayNameEn":"BTC_USDT SWAP",
            //                 "positionOpenType":3,
            //                 "baseCoin":"BTC",
            //                 "quoteCoin":"USDT",
            //                 "settleCoin":"USDT",
            //                 "contractSize":0.0001,
            //                 "minLeverage":1,
            //                 "maxLeverage":125,
            //                 "priceScale":2, // seems useless atm,'s just how UI shows the price, $i->e. 29583.50 for BTC/USDT:USDT, while price ticksize is 0.5
            //                 "volScale":0, // probably => contract amount precision
            //                 "amountScale":4, // probably => $quote currency precision
            //                 "priceUnit":0.5, // price tick size
            //                 "volUnit":1, // probably => contract tick size
            //                 "minVol":1,
            //                 "maxVol":1000000,
            //                 "bidLimitPriceRate":0.1,
            //                 "askLimitPriceRate":0.1,
            //                 "takerFeeRate":0.0006,
            //                 "makerFeeRate":0.0002,
            //                 "maintenanceMarginRate":0.004,
            //                 "initialMarginRate":0.008,
            //                 "riskBaseVol":10000,
            //                 "riskIncrVol":200000,
            //                 "riskIncrMmr":0.004,
            //                 "riskIncrImr":0.004,
            //                 "riskLevelLimit":5,
            //                 "priceCoefficientVariation":0.1,
            //                 "indexOrigin":["BINANCE","GATEIO","HUOBI","MXC"],
            //                 "state":0, // 0 enabled, 1 delivery, 2 completed, 3 offline, 4 pause
            //                 "isNew":false,
            //                 "isHot":true,
            //                 "isHidden":false
            //             ),
            //         ]
            //     }
            //
            $data = $this->safe_value($response, 'data', array());
            $result = array();
            for ($i = 0; $i < count($data); $i++) {
                $market = $data[$i];
                $id = $this->safe_string($market, 'symbol');
                $baseId = $this->safe_string($market, 'baseCoin');
                $quoteId = $this->safe_string($market, 'quoteCoin');
                $settleId = $this->safe_string($market, 'settleCoin');
                $base = $this->safe_currency_code($baseId);
                $quote = $this->safe_currency_code($quoteId);
                $settle = $this->safe_currency_code($settleId);
                $state = $this->safe_string($market, 'state');
                $isLinear = $quote === $settle;
                $result[] = array(
                    'id' => $id,
                    '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' => ($state === '0'),
                    'contract' => true,
                    'linear' => $isLinear,
                    'inverse' => !$isLinear,
                    'taker' => $this->safe_number($market, 'takerFeeRate'),
                    'maker' => $this->safe_number($market, 'makerFeeRate'),
                    'contractSize' => $this->safe_number($market, 'contractSize'),
                    'expiry' => null,
                    'expiryDatetime' => null,
                    'strike' => null,
                    'optionType' => null,
                    'precision' => array(
                        'amount' => $this->safe_number($market, 'volUnit'),
                        'price' => $this->safe_number($market, 'priceUnit'),
                    ),
                    'limits' => array(
                        'leverage' => array(
                            'min' => $this->safe_number($market, 'minLeverage'),
                            'max' => $this->safe_number($market, 'maxLeverage'),
                        ),
                        'amount' => array(
                            'min' => $this->safe_number($market, 'minVol'),
                            'max' => $this->safe_number($market, 'maxVol'),
                        ),
                        'price' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'cost' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                    'created' => null,
                    'info' => $market,
                );
            }
            return $result;
        }) ();
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $limit, $params) {
            /**
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#order-book
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-contract-s-depth-information
             *
             * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other $data
             * @param {string} $symbol unified $symbol of the $market to fetch the order book for
             * @param {int} [$limit] the maximum amount of order book entries to return
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $orderbook = null;
            if ($market['spot']) {
                $response = Async\await($this->spotPublicGetDepth ($this->extend($request, $params)));
                //
                //     {
                //         "lastUpdateId" => "744267132",
                //         "bids" => [
                //             ["40838.50","0.387864"],
                //             ["40837.95","0.008400"],
                //         ],
                //         "asks" => [
                //             ["40838.61","6.544908"],
                //             ["40838.88","0.498000"],
                //         ]
                //     }
                //
                $spotTimestamp = $this->safe_integer($response, 'timestamp');
                $orderbook = $this->parse_order_book($response, $symbol, $spotTimestamp);
                $orderbook['nonce'] = $this->safe_integer($response, 'lastUpdateId');
            } elseif ($market['swap']) {
                $response = Async\await($this->contractPublicGetDepthSymbol ($this->extend($request, $params)));
                //
                //     {
                //         "success":true,
                //         "code":0,
                //         "data":{
                //             "asks":[
                //                 [3445.72,48379,1],
                //                 [3445.75,34994,1],
                //             ],
                //             "bids":[
                //                 [3445.55,44081,1],
                //                 [3445.51,24857,1],
                //             ],
                //             "version":2827730444,
                //             "timestamp":1634117846232
                //         }
                //     }
                //
                $data = $this->safe_value($response, 'data');
                $timestamp = $this->safe_integer($data, 'timestamp');
                $orderbook = $this->parse_order_book($data, $symbol, $timestamp);
                $orderbook['nonce'] = $this->safe_integer($data, 'version');
            }
            return $orderbook;
        }) ();
    }

    public function parse_bid_ask($bidask, int|string $priceKey = 0, int|string $amountKey = 1, int|string $countOrIdKey = 2) {
        $countKey = 2;
        $price = $this->safe_number($bidask, $priceKey);
        $amount = $this->safe_number($bidask, $amountKey);
        $count = $this->safe_number($bidask, $countKey);
        if ($count !== null) {
            return array( $price, $amount, $count );
        }
        return array( $price, $amount );
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#recent-$trades-list
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#compressed-aggregate-$trades-list
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-contract-transaction-data
             *
             * get the list of most recent $trades for a particular $symbol
             * @param {string} $symbol unified $symbol of the $market to fetch $trades for
             * @param {int} [$since] timestamp in ms of the earliest trade to fetch
             * @param {int} [$limit] the maximum amount of $trades to fetch
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] *spot only* *$since must be defined* the latest time in ms to fetch entries for
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-$trades trade structures~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            if ($limit !== null) {
                $request['limit'] = $limit;
            }
            $trades = null;
            if ($market['spot']) {
                $until = $this->safe_integer_n($params, array( 'endTime', 'until' ));
                if ($since !== null) {
                    $request['startTime'] = $since;
                    if ($until === null) {
                        throw new ArgumentsRequired($this->id . ' fetchTrades() requires an $until parameter when $since is provided');
                    }
                }
                if ($until !== null) {
                    if ($since === null) {
                        throw new ArgumentsRequired($this->id . ' fetchTrades() requires a $since parameter when $until is provided');
                    }
                    $request['endTime'] = $until;
                }
                $method = $this->safe_string($this->options, 'fetchTradesMethod', 'spotPublicGetAggTrades');
                $method = $this->safe_string($params, 'method', $method); // AggTrades, HistoricalTrades, Trades
                $params = $this->omit($params, array( 'method' ));
                if ($method === 'spotPublicGetAggTrades') {
                    $trades = Async\await($this->spotPublicGetAggTrades ($this->extend($request, $params)));
                } elseif ($method === 'spotPublicGetHistoricalTrades') {
                    $trades = Async\await($this->spotPublicGetHistoricalTrades ($this->extend($request, $params)));
                } elseif ($method === 'spotPublicGetTrades') {
                    $trades = Async\await($this->spotPublicGetTrades ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchTrades() not support this method');
                }
                //
                //     /trades, /historicalTrades
                //
                //     array(
                //         array(
                //             "id" => null,
                //             "price" => "40798.94",
                //             "qty" => "0.000508",
                //             "quoteQty" => "20.72586152",
                //             "time" => "1647546934374",
                //             "isBuyerMaker" => true,
                //             "isBestMatch" => true
                //         ),
                //     )
                //
                //     /aggrTrades
                //
                //     array(
                //         array(
                //           "a" => null,
                //           "f" => null,
                //           "l" => null,
                //           "p" => "40679",
                //           "q" => "0.001309",
                //           "T" => 1647551328000,
                //           "m" => true,
                //           "M" => true
                //         ),
                //     )
                //
            } elseif ($market['swap']) {
                $response = Async\await($this->contractPublicGetDealsSymbol ($this->extend($request, $params)));
                //
                //     {
                //         "success" => true,
                //         "code" => 0,
                //         "data" => array(
                //             array(
                //                 "p" => 31199,
                //                 "v" => 18,
                //                 "T" => 1,
                //                 "O" => 3,
                //                 "M" => 2,
                //                 "t" => 1609831235985
                //             ),
                //         )
                //     }
                //
                $trades = $this->safe_value($response, 'data');
            }
            return $this->parse_trades($trades, $market, $since, $limit);
        }) ();
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        $id = null;
        $timestamp = null;
        $orderId = null;
        $symbol = null;
        $fee = null;
        $type = null;
        $side = null;
        $takerOrMaker = null;
        $priceString = null;
        $amountString = null;
        $costString = null;
        // if swap
        if (is_array($trade) && array_key_exists('v', $trade)) {
            //
            // swap => fetchTrades
            //
            //     {
            //         "p" => 31199,
            //         "v" => 18,
            //         "T" => 1,
            //         "O" => 3,
            //         "M" => 2,
            //         "t" => 1609831235985
            //     }
            //
            $timestamp = $this->safe_integer($trade, 't');
            $market = $this->safe_market(null, $market);
            $symbol = $market['symbol'];
            $priceString = $this->safe_string($trade, 'p');
            $amountString = $this->safe_string($trade, 'v');
            $side = $this->parse_order_side($this->safe_string($trade, 'T'));
            $takerOrMaker = 'taker';
        } else {
            //
            // spot => fetchTrades (for aggTrades)
            //
            //         {
            //             "a" => null,
            //             "f" => null,
            //             "l" => null,
            //             "p" => "40679",
            //             "q" => "0.001309",
            //             "T" => 1647551328000,
            //             "m" => true,
            //             "M" => true
            //         }
            //
            // spot => fetchMyTrades, fetchOrderTrades
            //
            //         {
            //             "symbol" => "BTCUSDT",
            //             "id" => "133948532984922113",
            //             "orderId" => "133948532531949568",
            //             "orderListId" => "-1",
            //             "price" => "41995.51",
            //             "qty" => "0.0002",
            //             "quoteQty" => "8.399102",
            //             "commission" => "0.016798204",
            //             "commissionAsset" => "USDT",
            //             "time" => "1647718055000",
            //             "isBuyer" => true,
            //             "isMaker" => false,
            //             "isBestMatch" => true
            //         }
            //
            // swap => fetchMyTrades, fetchOrderTrades
            //
            //         {
            //             "id" => "299444585",
            //             "symbol" => "STEPN_USDT",
            //             "side" => "1",
            //             "vol" => "1",
            //             "price" => "2.45455",
            //             "feeCurrency" => "USDT",
            //             "fee" => "0.00147273",
            //             "timestamp" => "1648924557000",
            //             "profit" => "0",
            //             "category" => "1",
            //             "orderId" => "265307163526610432",
            //             "positionMode" => "1",
            //             "taker" => true
            //         }
            //
            $marketId = $this->safe_string($trade, 'symbol');
            $market = $this->safe_market($marketId, $market);
            $symbol = $market['symbol'];
            $id = $this->safe_string_2($trade, 'id', 'a');
            $priceString = $this->safe_string_2($trade, 'price', 'p');
            $orderId = $this->safe_string($trade, 'orderId');
            // if swap
            if (is_array($trade) && array_key_exists('positionMode', $trade)) {
                $timestamp = $this->safe_integer($trade, 'timestamp');
                $amountString = $this->safe_string($trade, 'vol');
                $side = $this->parse_order_side($this->safe_string($trade, 'side'));
                $fee = array(
                    'cost' => $this->safe_string($trade, 'fee'),
                    'currency' => $this->safe_currency_code($this->safe_string($trade, 'feeCurrency')),
                );
                $takerOrMaker = $this->safe_value($trade, 'taker') ? 'taker' : 'maker';
            } else {
                $timestamp = $this->safe_integer_2($trade, 'time', 'T');
                $amountString = $this->safe_string_2($trade, 'qty', 'q');
                $costString = $this->safe_string($trade, 'quoteQty');
                $isBuyer = $this->safe_value($trade, 'isBuyer');
                $isMaker = $this->safe_value($trade, 'isMaker');
                $buyerMaker = $this->safe_value_2($trade, 'isBuyerMaker', 'm');
                if ($isMaker !== null) {
                    $takerOrMaker = $isMaker ? 'maker' : 'taker';
                }
                if ($isBuyer !== null) {
                    $side = $isBuyer ? 'buy' : 'sell';
                }
                if ($buyerMaker !== null) {
                    $side = $buyerMaker ? 'sell' : 'buy';
                    $takerOrMaker = 'taker';
                }
                $feeAsset = $this->safe_string($trade, 'commissionAsset');
                if ($feeAsset !== null) {
                    $fee = array(
                        'cost' => $this->safe_string($trade, 'commission'),
                        'currency' => $this->safe_currency_code($feeAsset),
                    );
                }
            }
        }
        if ($id === null && $this->safe_bool($this->options, 'useCcxtTradeId', true)) {
            $id = $this->create_ccxt_trade_id($timestamp, $side, $amountString, $priceString, $takerOrMaker);
        }
        return $this->safe_trade(array(
            'id' => $id,
            'order' => $orderId,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $symbol,
            'type' => $type,
            'side' => $side,
            'takerOrMaker' => $takerOrMaker,
            'price' => $priceString,
            'amount' => $amountString,
            'cost' => $costString,
            'fee' => $fee,
            'info' => $trade,
        ), $market);
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $timeframe, $since, $limit, $params) {
            /**
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#kline-candlestick-$data
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#k-line-$data
             *
             * fetches historical candlestick $data containing the open, high, low, and close price, and the volume of a $market
             * @param {string} $symbol unified $symbol of the $market to fetch OHLCV $data for
             * @param {string} $timeframe the length of time each candle represents
             * @param {int} [$since] timestamp in ms of the earliest candle to fetch
             * @param {int} [$limit] the maximum amount of $candles to fetch
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] timestamp in ms of the latest candle to fetch
             * @param {boolean} [$params->paginate] default false, when true will automatically $paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-$params)
             * @return {int[][]} A list of $candles ordered, open, high, low, close, volume
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $maxLimit = ($market['spot']) ? 1000 : 2000;
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchOHLCV', 'paginate', false);
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_deterministic('fetchOHLCV', $symbol, $since, $limit, $timeframe, $params, $maxLimit));
            }
            $options = $this->safe_value($this->options, 'timeframes', array());
            $timeframes = $this->safe_value($options, $market['type'], array());
            $timeframeValue = $this->safe_string($timeframes, $timeframe);
            $duration = $this->parse_timeframe($timeframe) * 1000;
            $request = array(
                'symbol' => $market['id'],
                'interval' => $timeframeValue,
            );
            $candles = null;
            if ($market['spot']) {
                $until = $this->safe_integer_n($params, array( 'until', 'endTime' ));
                if ($since !== null) {
                    $request['startTime'] = $since;
                    if ($until === null) {
                        // we have to calculate it assuming we can get at most 2000 entries per $request
                        $end = $this->sum($since, $maxLimit * $duration);
                        $now = $this->milliseconds();
                        $request['endTime'] = min ($end, $now);
                    }
                }
                if ($limit !== null) {
                    $request['limit'] = $limit;
                }
                if ($until !== null) {
                    $params = $this->omit($params, array( 'until' ));
                    $request['endTime'] = $until;
                }
                $response = Async\await($this->spotPublicGetKlines ($this->extend($request, $params)));
                //
                //     array(
                //       array(
                //         1640804880000,
                //         "47482.36",
                //         "47482.36",
                //         "47416.57",
                //         "47436.1",
                //         "3.550717",
                //         1640804940000,
                //         "168387.3"
                //       ),
                //     )
                //
                $candles = $response;
            } elseif ($market['swap']) {
                $until = $this->safe_integer_product_n($params, array( 'until', 'endTime' ), 0.001);
                if ($since !== null) {
                    $request['start'] = $this->parse_to_int($since / 1000);
                }
                if ($until !== null) {
                    $params = $this->omit($params, array( 'until' ));
                    $request['end'] = $until;
                }
                $priceType = $this->safe_string($params, 'price', 'default');
                $params = $this->omit($params, 'price');
                $response = null;
                if ($priceType === 'default') {
                    $response = Async\await($this->contractPublicGetKlineSymbol ($this->extend($request, $params)));
                } elseif ($priceType === 'index') {
                    $response = Async\await($this->contractPublicGetKlineIndexPriceSymbol ($this->extend($request, $params)));
                } elseif ($priceType === 'mark') {
                    $response = Async\await($this->contractPublicGetKlineFairPriceSymbol ($this->extend($request, $params)));
                } else {
                    throw new NotSupported($this->id . ' fetchOHLCV() not support this price type, [default, index, mark]');
                }
                //
                //     {
                //         "success":true,
                //         "code":0,
                //         "data":{
                //             "time":[1634052300,1634052360,1634052420],
                //             "open":[3492.2,3491.3,3495.65],
                //             "close":[3491.3,3495.65,3495.2],
                //             "high":[3495.85,3496.55,3499.4],
                //             "low":[3491.15,3490.9,3494.2],
                //             "vol":[1740.0,351.0,314.0],
                //             "amount":[60793.623,12260.4885,10983.1375],
                //         }
                //     }
                //
                $data = $this->safe_value($response, 'data');
                $candles = $this->convert_trading_view_to_ohlcv($data, 'time', 'open', 'high', 'low', 'close', 'vol');
            }
            return $this->parse_ohlcvs($candles, $market, $timeframe, $since, $limit);
        }) ();
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        return array(
            $this->safe_integer($ohlcv, 0),
            $this->safe_number($ohlcv, 1),
            $this->safe_number($ohlcv, 2),
            $this->safe_number($ohlcv, 3),
            $this->safe_number($ohlcv, 4),
            $this->safe_number($ohlcv, 5),
        );
    }

    public function fetch_tickers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches price $tickers for multiple markets, statistical information calculated over the past 24 hours for each $market
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#24hr-ticker-price-change-statistics
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-contract-trend-data
             *
             * @param {string[]|null} $symbols unified $symbols of the markets to fetch the ticker for, all $market $tickers are returned if not assigned
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            $isSingularMarket = false;
            if ($symbols !== null) {
                $length = count($symbols);
                $isSingularMarket = $length === 1;
                $firstSymbol = $this->safe_string($symbols, 0);
                $market = $this->market($firstSymbol);
            }
            list($marketType, $query) = $this->handle_market_type_and_params('fetchTickers', $market, $params);
            $tickers = null;
            if ($isSingularMarket) {
                $request['symbol'] = $market['id'];
            }
            if ($marketType === 'spot') {
                $tickers = Async\await($this->spotPublicGetTicker24hr ($this->extend($request, $query)));
                //
                //     array(
                //         {
                //             "symbol" => "BTCUSDT",
                //             "priceChange" => "184.34",
                //             "priceChangePercent" => "0.00400048",
                //             "prevClosePrice" => "46079.37",
                //             "lastPrice" => "46263.71",
                //             "lastQty" => "",
                //             "bidPrice" => "46260.38",
                //             "bidQty" => "",
                //             "askPrice" => "46260.41",
                //             "askQty" => "",
                //             "openPrice" => "46079.37",
                //             "highPrice" => "47550.01",
                //             "lowPrice" => "45555.5",
                //             "volume" => "1732.461487",
                //             "quoteVolume" => null,
                //             "openTime" => 1641349500000,
                //             "closeTime" => 1641349582808,
                //             "count" => null
                //         }
                //     )
                //
            } elseif ($marketType === 'swap') {
                $response = Async\await($this->contractPublicGetTicker ($this->extend($request, $query)));
                //
                //     {
                //         "success":true,
                //         "code":0,
                //         "data":array(
                //             array(
                //                 "symbol":"ETH_USDT",
                //                 "lastPrice":3581.3,
                //                 "bid1":3581.25,
                //                 "ask1":3581.5,
                //                 "volume24":4045530,
                //                 "amount24":141331823.5755,
                //                 "holdVol":5832946,
                //                 "lower24Price":3413.4,
                //                 "high24Price":3588.7,
                //                 "riseFallRate":0.0275,
                //                 "riseFallValue":95.95,
                //                 "indexPrice":3580.7852,
                //                 "fairPrice":3581.08,
                //                 "fundingRate":0.000063,
                //                 "maxBidPrice":3938.85,
                //                 "minAskPrice":3222.7,
                //                 "timestamp":1634162885016
                //             ),
                //         )
                //     }
                //
                $tickers = $this->safe_value($response, 'data', array());
            }
            // when it's single symbol $request, the returned structure is different (singular object) for both spot & swap, thus we need to wrap inside array
            if ($isSingularMarket) {
                $tickers = array( $tickers );
            }
            return $this->parse_tickers($tickers, $symbols);
        }) ();
    }

    public function fetch_ticker(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetches a price $ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#24hr-$ticker-price-change-statistics
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-contract-trend-data
             *
             * @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~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            list($marketType, $query) = $this->handle_market_type_and_params('fetchTicker', $market, $params);
            $ticker = null;
            $request = array(
                'symbol' => $market['id'],
            );
            if ($marketType === 'spot') {
                $ticker = Async\await($this->spotPublicGetTicker24hr ($this->extend($request, $query)));
                //
                //     {
                //         "symbol" => "BTCUSDT",
                //         "priceChange" => "184.34",
                //         "priceChangePercent" => "0.00400048",
                //         "prevClosePrice" => "46079.37",
                //         "lastPrice" => "46263.71",
                //         "lastQty" => "",
                //         "bidPrice" => "46260.38",
                //         "bidQty" => "",
                //         "askPrice" => "46260.41",
                //         "askQty" => "",
                //         "openPrice" => "46079.37",
                //         "highPrice" => "47550.01",
                //         "lowPrice" => "45555.5",
                //         "volume" => "1732.461487",
                //         "quoteVolume" => null,
                //         "openTime" => 1641349500000,
                //         "closeTime" => 1641349582808,
                //         "count" => null
                //     }
                //
            } elseif ($marketType === 'swap') {
                $response = Async\await($this->contractPublicGetTicker ($this->extend($request, $query)));
                //
                //     {
                //         "success":true,
                //         "code":0,
                //         "data":{
                //             "symbol":"ETH_USDT",
                //             "lastPrice":3581.3,
                //             "bid1":3581.25,
                //             "ask1":3581.5,
                //             "volume24":4045530,
                //             "amount24":141331823.5755,
                //             "holdVol":5832946,
                //             "lower24Price":3413.4,
                //             "high24Price":3588.7,
                //             "riseFallRate":0.0275,
                //             "riseFallValue":95.95,
                //             "indexPrice":3580.7852,
                //             "fairPrice":3581.08,
                //             "fundingRate":0.000063,
                //             "maxBidPrice":3938.85,
                //             "minAskPrice":3222.7,
                //             "timestamp":1634162885016
                //         }
                //     }
                //
                $ticker = $this->safe_value($response, 'data', array());
            }
            // when it's single $symbol $request, the returned structure is different (singular object) for both spot & swap, thus we need to wrap inside array
            return $this->parse_ticker($ticker, $market);
        }) ();
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        $marketId = $this->safe_string($ticker, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $timestamp = null;
        $bid = null;
        $ask = null;
        $bidVolume = null;
        $askVolume = null;
        $baseVolume = null;
        $quoteVolume = null;
        $open = null;
        $high = null;
        $low = null;
        $changePcnt = null;
        $changeValue = null;
        $prevClose = null;
        $isSwap = $this->safe_value($market, 'swap');
        // if swap
        if ($isSwap || (is_array($ticker) && array_key_exists('timestamp', $ticker))) {
            //
            //     {
            //         "symbol" => "ETH_USDT",
            //         "lastPrice" => 3581.3,
            //         "bid1" => 3581.25,
            //         "ask1" => 3581.5,
            //         "volume24" => 4045530,
            //         "amount24" => 141331823.5755,
            //         "holdVol" => 5832946,
            //         "lower24Price" => 3413.4,
            //         "high24Price" => 3588.7,
            //         "riseFallRate" => 0.0275,
            //         "riseFallValue" => 95.95,
            //         "indexPrice" => 3580.7852,
            //         "fairPrice" => 3581.08,
            //         "fundingRate" => 0.000063,
            //         "maxBidPrice" => 3938.85,
            //         "minAskPrice" => 3222.7,
            //         "timestamp" => 1634162885016
            //     }
            //
            $timestamp = $this->safe_integer($ticker, 'timestamp');
            $bid = $this->safe_string($ticker, 'bid1');
            $ask = $this->safe_string($ticker, 'ask1');
            $baseVolume = $this->safe_string($ticker, 'volume24');
            $quoteVolume = $this->safe_string($ticker, 'amount24');
            $high = $this->safe_string($ticker, 'high24Price');
            $low = $this->safe_string($ticker, 'lower24Price');
            $changeValue = $this->safe_string($ticker, 'riseFallValue');
            $changePcnt = $this->safe_string($ticker, 'riseFallRate');
            $changePcnt = Precise::string_mul($changePcnt, '100');
        } else {
            //
            //     {
            //         "symbol" => "BTCUSDT",
            //         "priceChange" => "184.34",
            //         "priceChangePercent" => "0.00400048",
            //         "prevClosePrice" => "46079.37",
            //         "lastPrice" => "46263.71",
            //         "lastQty" => "",
            //         "bidPrice" => "46260.38",
            //         "bidQty" => "",
            //         "askPrice" => "46260.41",
            //         "askQty" => "",
            //         "openPrice" => "46079.37",
            //         "highPrice" => "47550.01",
            //         "lowPrice" => "45555.5",
            //         "volume" => "1732.461487",
            //         "quoteVolume" => null,
            //         "openTime" => 1641349500000,
            //         "closeTime" => 1641349582808,
            //         "count" => null
            //     }
            //
            $timestamp = $this->safe_integer($ticker, 'closeTime');
            $bid = $this->safe_string($ticker, 'bidPrice');
            $ask = $this->safe_string($ticker, 'askPrice');
            $bidVolume = $this->safe_string($ticker, 'bidQty');
            $askVolume = $this->safe_string($ticker, 'askQty');
            if (Precise::string_eq($bidVolume, '0')) {
                $bidVolume = null;
            }
            if (Precise::string_eq($askVolume, '0')) {
                $askVolume = null;
            }
            $baseVolume = $this->safe_string($ticker, 'volume');
            $quoteVolume = $this->safe_string($ticker, 'quoteVolume');
            $open = $this->safe_string($ticker, 'openPrice');
            $high = $this->safe_string($ticker, 'highPrice');
            $low = $this->safe_string($ticker, 'lowPrice');
            $prevClose = $this->safe_string($ticker, 'prevClosePrice');
            $changeValue = $this->safe_string($ticker, 'priceChange');
            $changePcnt = $this->safe_string($ticker, 'priceChangePercent');
            $changePcnt = Precise::string_mul($changePcnt, '100');
        }
        return $this->safe_ticker(array(
            'symbol' => $market['symbol'],
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'open' => $open,
            'high' => $high,
            'low' => $low,
            'close' => $this->safe_string($ticker, 'lastPrice'),
            'bid' => $bid,
            'bidVolume' => $bidVolume,
            'ask' => $ask,
            'askVolume' => $askVolume,
            'vwap' => null,
            'previousClose' => $prevClose,
            'change' => $changeValue,
            'percentage' => $changePcnt,
            'average' => null,
            'baseVolume' => $baseVolume,
            'quoteVolume' => $quoteVolume,
            'info' => $ticker,
        ), $market);
    }

    public function fetch_bids_asks(?array $symbols = null, $params = array ()) {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetches the bid and ask price and volume for multiple markets
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#symbol-order-book-ticker
             *
             * @param {string[]|null} $symbols unified $symbols of the markets to fetch the bids and asks for, all markets 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~
             */
            Async\await($this->load_markets());
            $market = null;
            $isSingularMarket = false;
            if ($symbols !== null) {
                $length = count($symbols);
                $isSingularMarket = $length === 1;
                $market = $this->market($symbols[0]);
            }
            list($marketType, $query) = $this->handle_market_type_and_params('fetchBidsAsks', $market, $params);
            $tickers = null;
            if ($marketType === 'spot') {
                $tickers = Async\await($this->spotPublicGetTickerBookTicker ($query));
                //
                //     array(
                //       array(
                //         "symbol" => "AEUSDT",
                //         "bidPrice" => "0.11001",
                //         "bidQty" => "115.59",
                //         "askPrice" => "0.11127",
                //         "askQty" => "215.48"
                //       ),
                //     )
                //
            } elseif ($marketType === 'swap') {
                throw new NotSupported($this->id . ' fetchBidsAsks() is not available for ' . $marketType . ' markets');
            }
            // when it's single symbol request, the returned structure is different (singular object) for both spot & swap, thus we need to wrap inside array
            if ($isSingularMarket) {
                $tickers = array( $tickers );
            }
            return $this->parse_tickers($tickers, $symbols);
        }) ();
    }

    public function create_market_buy_order_with_cost(string $symbol, float $cost, $params = array ()) {
        return Async\async(function () use ($symbol, $cost, $params) {
            /**
             * create a $market buy order by providing the $symbol and $cost
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#new-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~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['spot']) {
                throw new NotSupported($this->id . ' createMarketBuyOrderWithCost() supports spot orders only');
            }
            $req = array(
                'cost' => $cost,
            );
            return Async\await($this->create_order($symbol, 'market', 'buy', 0, null, $this->extend($req, $params)));
        }) ();
    }

    public function create_market_sell_order_with_cost(string $symbol, float $cost, $params = array ()) {
        return Async\async(function () use ($symbol, $cost, $params) {
            /**
             * create a $market sell order by providing the $symbol and $cost
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#new-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~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['spot']) {
                throw new NotSupported($this->id . ' createMarketBuyOrderWithCost() supports spot orders only');
            }
            $req = array(
                'cost' => $cost,
            );
            return Async\await($this->create_order($symbol, 'market', 'sell', 0, null, $this->extend($req, $params)));
        }) ();
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade order
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#new-order
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#order-under-maintenance
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#trigger-order-under-maintenance
             *
             * @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 {string} [$params->marginMode] only 'isolated' is supported for spot-margin trading
             * @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 if it will be a maker order
             * @param {bool} [$params->reduceOnly] *contract only* indicates if this order is to reduce the size of a position
             * @param {bool} [$params->hedged] *swap only* true for hedged mode, false for one way mode, default is false
             * @param {string} [$params->timeInForce] 'IOC' or 'FOK', default is 'GTC'
             * EXCHANGE SPECIFIC PARAMETERS
             * @param {int} [$params->leverage] *contract only* leverage is necessary on isolated margin
             * @param {long} [$params->positionId] *contract only* it is recommended to property_exists($this, fill) parameter when closing a position
             * @param {string} [$params->externalOid] *contract only* external order ID
             * @param {int} [$params->positionMode] *contract only*  1:hedge, 2:one-way, default => the user's current config
             * @param {boolean} [$params->test] *spot only* whether to use the test endpoint or not, default is false
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            list($marginMode, $query) = $this->handle_margin_mode_and_params('createOrder', $params);
            if ($market['spot']) {
                return Async\await($this->create_spot_order($market, $type, $side, $amount, $price, $marginMode, $query));
            } else {
                return Async\await($this->create_swap_order($market, $type, $side, $amount, $price, $marginMode, $query));
            }
        }) ();
    }

    public function create_spot_order_request($market, $type, $side, $amount, $price = null, $marginMode = null, $params = array ()) {
        $symbol = $market['symbol'];
        $orderSide = strtoupper($side);
        $request = array(
            'symbol' => $market['id'],
            'side' => $orderSide,
            'type' => strtoupper($type),
        );
        if ($type === 'market') {
            $cost = $this->safe_number_2($params, 'cost', 'quoteOrderQty');
            $params = $this->omit($params, 'cost');
            if ($cost !== null) {
                $amount = $cost;
                $request['quoteOrderQty'] = $this->cost_to_precision($symbol, $amount);
            } else {
                if ($price === null) {
                    $request['quantity'] = $this->amount_to_precision($symbol, $amount);
                } else {
                    $amountString = $this->number_to_string($amount);
                    $priceString = $this->number_to_string($price);
                    $quoteAmount = Precise::string_mul($amountString, $priceString);
                    $amount = $quoteAmount;
                    $request['quoteOrderQty'] = $this->cost_to_precision($symbol, $amount);
                }
            }
        } else {
            $request['quantity'] = $this->amount_to_precision($symbol, $amount);
        }
        if ($price !== null) {
            $request['price'] = $this->price_to_precision($symbol, $price);
        }
        $clientOrderId = $this->safe_string($params, 'clientOrderId');
        if ($clientOrderId !== null) {
            $request['newClientOrderId'] = $clientOrderId;
            $params = $this->omit($params, array( 'type', 'clientOrderId' ));
        }
        if ($marginMode !== null) {
            if ($marginMode !== 'isolated') {
                throw new BadRequest($this->id . ' createOrder() does not support $marginMode ' . $marginMode . ' for spot-margin trading');
            }
        }
        $postOnly = null;
        list($postOnly, $params) = $this->handle_post_only($type === 'market', $type === 'LIMIT_MAKER', $params);
        if ($postOnly) {
            $request['type'] = 'LIMIT_MAKER';
        }
        $tif = $this->safe_string($params, 'timeInForce');
        if ($tif !== null) {
            $params = $this->omit($params, 'timeInForce');
            if ($tif === 'IOC') {
                $request['type'] = 'IMMEDIATE_OR_CANCEL';
            } elseif ($tif === 'FOK') {
                $request['type'] = 'FILL_OR_KILL';
            }
        }
        return $this->extend($request, $params);
    }

    public function create_spot_order($market, $type, $side, $amount, $price = null, $marginMode = null, $params = array ()) {
        return Async\async(function () use ($market, $type, $side, $amount, $price, $marginMode, $params) {
            /**
             * @ignore
             * create a trade $order
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#new-$order
             *
             * @param {string} $market 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 {string} [$marginMode] only 'isolated' is supported for spot-margin trading
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {bool} [$params->postOnly] if true, the $order will only be posted if it will be a maker $order
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=$order-structure $order structure~
             */
            Async\await($this->load_markets());
            $test = $this->safe_bool($params, 'test', false);
            $params = $this->omit($params, 'test');
            $request = $this->create_spot_order_request($market, $type, $side, $amount, $price, $marginMode, $params);
            $response = null;
            if ($test) {
                $response = Async\await($this->spotPrivatePostOrderTest ($request));
            } else {
                $response = Async\await($this->spotPrivatePostOrder ($request));
            }
            //
            // spot
            //
            //     {
            //         "symbol" => "BTCUSDT",
            //         "orderId" => "123738410679123456",
            //         "orderListId" => -1
            //     }
            //
            // margin
            //
            //     {
            //         "symbol" => "BTCUSDT",
            //         "orderId" => "762634301354414080",
            //         "clientOrderId" => null,
            //         "isIsolated" => true,
            //         "transactTime" => 1661992652132
            //     }
            //
            $order = $this->parse_order($response, $market);
            $order['side'] = $side;
            $order['type'] = $type;
            if ($this->safe_string($order, 'price') === null) {
                $order['price'] = $price;
            }
            if ($this->safe_string($order, 'amount') === null) {
                $order['amount'] = $amount;
            }
            return $order;
        }) ();
    }

    public function create_swap_order($market, $type, $side, $amount, $price = null, $marginMode = null, $params = array ()) {
        return Async\async(function () use ($market, $type, $side, $amount, $price, $marginMode, $params) {
            /**
             * @ignore
             * create a trade order
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#new-order
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#order-under-maintenance
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#trigger-order-under-maintenance
             *
             * @param {string} $market 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 {string} [$marginMode] only 'isolated' is supported for spot-margin trading
             * @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 if it will be a maker order
             * @param {bool} [$params->reduceOnly] indicates if this order is to reduce the size of a position
             * @param {bool} [$params->hedged] *swap only* true for $hedged mode, false for one way mode, default is false
             *
             * EXCHANGE SPECIFIC PARAMETERS
             * @param {int} [$params->leverage] $leverage is necessary on isolated margin
             * @param {long} [$params->positionId] it is recommended to property_exists($this, fill) parameter when closing a position
             * @param {string} [$params->externalOid] external order ID
             * @param {int} [$params->positionMode] 1:hedge, 2:one-way, default => the user's current config
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $symbol = $market['symbol'];
            $unavailableContracts = $this->safe_value($this->options, 'unavailableContracts', array());
            $isContractUnavaiable = $this->safe_bool($unavailableContracts, $symbol, false);
            if ($isContractUnavaiable) {
                throw new NotSupported($this->id . ' createSwapOrder() does not support yet this $symbol:' . $symbol);
            }
            $openType = null;
            if ($marginMode !== null) {
                if ($marginMode === 'cross') {
                    $openType = 2;
                } elseif ($marginMode === 'isolated') {
                    $openType = 1;
                } else {
                    throw new ArgumentsRequired($this->id . ' createSwapOrder() $marginMode parameter should be either "cross" or "isolated"');
                }
            } else {
                $openType = $this->safe_integer($params, 'openType', 2); // defaulting to cross margin
            }
            if (($type !== 'limit') && ($type !== 'market') && ($type !== 1) && ($type !== 2) && ($type !== 3) && ($type !== 4) && ($type !== 5) && ($type !== 6)) {
                throw new InvalidOrder($this->id . ' createSwapOrder() order $type must either limit, $market, or 1 for limit orders, 2 for post-only orders, 3 for IOC orders, 4 for FOK orders, 5 for $market orders or 6 to convert $market $price to current price');
            }
            $postOnly = null;
            list($postOnly, $params) = $this->handle_post_only($type === 'market', $type === 2, $params);
            if ($postOnly) {
                $type = 2;
            } elseif ($type === 'limit') {
                $type = 1;
            } elseif ($type === 'market') {
                $type = 6;
            }
            $request = array(
                'symbol' => $market['id'],
                // 'price' => floatval($this->price_to_precision($symbol, $price)),
                'vol' => floatval($this->amount_to_precision($symbol, $amount)),
                // 'leverage' => int, // required for isolated margin
                // 'side' => $side, // 1 open long, 2 close short, 3 open short, 4 close long
                //
                // supported order types
                //
                //     1 limit
                //     2 post only maker (PO)
                //     3 transact or cancel instantly (IOC)
                //     4 transact completely or cancel completely (FOK)
                //     5 $market orders
                //     6 convert $market $price to current $price
                //
                'type' => $type,
                'openType' => $openType, // 1 isolated, 2 cross
                // 'positionId' => 1394650, // long, property_exists($this, filling) parameter when closing a position is recommended
                // 'externalOid' => $clientOrderId,
                // 'triggerPrice' => 10.0, // Required for trigger order
                // 'triggerType' => 1, // Required for trigger order 1 => more than or equal, 2 => less than or equal
                // 'executeCycle' => 1, // Required for trigger order 1 => 24 hours,2 => 7 days
                // 'trend' => 1, // Required for trigger order 1 => latest $price, 2 => fair $price, 3 => index $price
                // 'orderType' => 1, // Required for trigger order 1 => limit order,2:Post Only Maker,3 => close or cancel instantly ,4 => close or cancel completely,5 => Market order
            );
            if (($type !== 5) && ($type !== 6) && ($type !== 'market')) {
                $request['price'] = floatval($this->price_to_precision($symbol, $price));
            }
            if ($openType === 1) {
                $leverage = $this->safe_integer($params, 'leverage');
                if ($leverage === null) {
                    throw new ArgumentsRequired($this->id . ' createSwapOrder() requires a $leverage parameter for isolated margin orders');
                }
            }
            $reduceOnly = $this->safe_bool($params, 'reduceOnly', false);
            $hedged = $this->safe_bool($params, 'hedged', false);
            $sideInteger = null;
            if ($hedged) {
                if ($reduceOnly) {
                    $params = $this->omit($params, 'reduceOnly'); // $hedged mode does not accept this parameter
                    $side = ($side === 'buy') ? 'sell' : 'buy';
                }
                $sideInteger = ($side === 'buy') ? 1 : 3;
                $request['positionMode'] = 1;
            } else {
                if ($reduceOnly) {
                    $sideInteger = ($side === 'buy') ? 2 : 4;
                } else {
                    $sideInteger = ($side === 'buy') ? 1 : 3;
                }
            }
            $request['side'] = $sideInteger;
            $clientOrderId = $this->safe_string_2($params, 'clientOrderId', 'externalOid');
            if ($clientOrderId !== null) {
                $request['externalOid'] = $clientOrderId;
            }
            $triggerPrice = $this->safe_number_2($params, 'triggerPrice', 'stopPrice');
            $params = $this->omit($params, array( 'clientOrderId', 'externalOid', 'postOnly', 'stopPrice', 'triggerPrice', 'hedged' ));
            $response = null;
            if ($triggerPrice) {
                $request['triggerPrice'] = $this->price_to_precision($symbol, $triggerPrice);
                $request['triggerType'] = $this->safe_integer($params, 'triggerType', 1);
                $request['executeCycle'] = $this->safe_integer($params, 'executeCycle', 1);
                $request['trend'] = $this->safe_integer($params, 'trend', 1);
                $request['orderType'] = $this->safe_integer($params, 'orderType', 1);
                $response = Async\await($this->contractPrivatePostPlanorderPlace ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->contractPrivatePostOrderSubmit ($this->extend($request, $params)));
            }
            //
            // Swap
            //     array("code":200,"data":"2ff3163e8617443cb9c6fc19d42b1ca4")
            //
            // Trigger
            //     array("success":true,"code":0,"data":259208506303929856)
            //
            $data = $this->safe_string($response, 'data');
            return $this->safe_order(array( 'id' => $data ), $market);
        }) ();
    }

    public function create_orders(array $orders, $params = array ()) {
        return Async\async(function () use ($orders, $params) {
            /**
             * *spot only*  *all $orders must have the same $symbol* create a list of trade $orders
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#batch-$orders
             *
             * @param {Array} $orders list of $orders to create, each object should contain the parameters required by createOrder, namely $symbol, $type, $side, $amount, $price and $params
             * @param {array} [$params] extra parameters specific to api endpoint
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
             */
            Async\await($this->load_markets());
            $ordersRequests = array();
            $symbol = null;
            for ($i = 0; $i < count($orders); $i++) {
                $rawOrder = $orders[$i];
                $marketId = $this->safe_string($rawOrder, 'symbol');
                $market = $this->market($marketId);
                if (!$market['spot']) {
                    throw new NotSupported($this->id . ' createOrders() is only supported for spot markets');
                }
                if ($symbol === null) {
                    $symbol = $marketId;
                } else {
                    if ($symbol !== $marketId) {
                        throw new BadRequest($this->id . ' createOrders() requires all $orders to have the same symbol');
                    }
                }
                $type = $this->safe_string($rawOrder, 'type');
                $side = $this->safe_string($rawOrder, 'side');
                $amount = $this->safe_value($rawOrder, 'amount');
                $price = $this->safe_value($rawOrder, 'price');
                $orderParams = $this->safe_value($rawOrder, 'params', array());
                $marginMode = null;
                list($marginMode, $params) = $this->handle_margin_mode_and_params('createOrder', $params);
                $orderRequest = $this->create_spot_order_request($market, $type, $side, $amount, $price, $marginMode, $orderParams);
                $ordersRequests[] = $orderRequest;
            }
            $request = array(
                'batchOrders' => $this->json($ordersRequests),
            );
            $response = Async\await($this->spotPrivatePostBatchOrders ($request));
            //
            // array(
            //     array(
            //       "symbol" => "BTCUSDT",
            //       "orderId" => "1196315350023612316",
            //       "newClientOrderId" => "hio8279hbdsds",
            //       "orderListId" => -1
            //     ),
            //     array(
            //       "newClientOrderId" => "123456",
            //       "msg" => "The minimum transaction volume cannot be less than:0.5USDT",
            //       "code" => 30002
            //     ),
            //     {
            //       "symbol" => "BTCUSDT",
            //       "orderId" => "1196315350023612318",
            //       "orderListId" => -1
            //     }
            // )
            //
            return $this->parse_orders($response);
        }) ();
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * fetches information on an order made by the user
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#$query-order
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#$query-the-order-based-on-the-order-number
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $symbol of the $market the order was made in
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] only 'isolated' is supported, for spot-margin trading
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchOrder() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $data = null;
            if ($market['spot']) {
                $clientOrderId = $this->safe_string($params, 'clientOrderId');
                if ($clientOrderId !== null) {
                    $params = $this->omit($params, 'clientOrderId');
                    $request['origClientOrderId'] = $clientOrderId;
                } else {
                    $request['orderId'] = $id;
                }
                list($marginMode, $query) = $this->handle_margin_mode_and_params('fetchOrder', $params);
                if ($marginMode !== null) {
                    if ($marginMode !== 'isolated') {
                        throw new BadRequest($this->id . ' fetchOrder() does not support $marginMode ' . $marginMode . ' for spot-margin trading');
                    }
                    $data = Async\await($this->spotPrivateGetMarginOrder ($this->extend($request, $query)));
                } else {
                    $data = Async\await($this->spotPrivateGetOrder ($this->extend($request, $query)));
                }
                //
                // spot
                //
                //     {
                //         "symbol" => "BTCUSDT",
                //         "orderId" => "133734823834147272",
                //         "orderListId" => "-1",
                //         "clientOrderId" => null,
                //         "price" => "30000",
                //         "origQty" => "0.0002",
                //         "executedQty" => "0",
                //         "cummulativeQuoteQty" => "0",
                //         "status" => "CANCELED",
                //         "timeInForce" => null,
                //         "type" => "LIMIT",
                //         "side" => "BUY",
                //         "stopPrice" => null,
                //         "icebergQty" => null,
                //         "time" => "1647667102000",
                //         "updateTime" => "1647708567000",
                //         "isWorking" => true,
                //         "origQuoteOrderQty" => "6"
                //     }
                //
                // margin
                //
                //     {
                //         "symbol" => "BTCUSDT",
                //         "orderId" => "763307297891028992",
                //         "orderListId" => "-1",
                //         "clientOrderId" => null,
                //         "price" => "18000",
                //         "origQty" => "0.0014",
                //         "executedQty" => "0",
                //         "cummulativeQuoteQty" => "0",
                //         "status" => "NEW",
                //         "type" => "LIMIT",
                //         "side" => "BUY",
                //         "isIsolated" => true,
                //         "isWorking" => true,
                //         "time" => 1662153107000,
                //         "updateTime" => 1662153107000
                //     }
                //
            } elseif ($market['swap']) {
                $request['order_id'] = $id;
                $response = Async\await($this->contractPrivateGetOrderGetOrderId ($this->extend($request, $params)));
                //
                //     {
                //         "success" => true,
                //         "code" => "0",
                //         "data" => {
                //             "orderId" => "264995729269765120",
                //             "symbol" => "STEPN_USDT",
                //             "positionId" => "0",
                //             "price" => "2.2",
                //             "vol" => "15",
                //             "leverage" => "20",
                //             "side" => "1",
                //             "category" => "1",
                //             "orderType" => "1",
                //             "dealAvgPrice" => "0",
                //             "dealVol" => "0",
                //             "orderMargin" => "2.2528",
                //             "takerFee" => "0",
                //             "makerFee" => "0",
                //             "profit" => "0",
                //             "feeCurrency" => "USDT",
                //             "openType" => "1",
                //             "state" => "2",
                //             "externalOid" => "_m_0e9520c256744d64b942985189026d20",
                //             "errorCode" => "0",
                //             "usedMargin" => "0",
                //             "createTime" => "1648850305236",
                //             "updateTime" => "1648850305245",
                //             "positionMode" => "1"
                //         }
                //     }
                //
                $data = $this->safe_value($response, 'data');
            }
            return $this->parse_order($data, $market);
        }) ();
    }

    public function fetch_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple orders made by the user
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#all-orders
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-of-the-user-39-s-historical-orders
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#gets-the-trigger-order-list
             *
             * @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
             * @param {int} [$params->until] the latest time in ms to fetch orders for
             * @param {string} [$params->marginMode] only 'isolated' is supported, for spot-margin trading
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $until = $this->safe_integer($params, 'until');
            $params = $this->omit($params, 'until');
            list($marketType, $query) = $this->handle_market_type_and_params('fetchOrders', $market, $params);
            if ($marketType === 'spot') {
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' fetchOrders() requires a $symbol argument for spot market');
                }
                list($marginMode, $queryInner) = $this->handle_margin_mode_and_params('fetchOrders', $params);
                if ($since !== null) {
                    $request['startTime'] = $since;
                }
                if ($until !== null) {
                    $request['endTime'] = $until;
                }
                if ($limit !== null) {
                    $request['limit'] = $limit;
                }
                $response = null;
                if ($marginMode !== null) {
                    if ($marginMode !== 'isolated') {
                        throw new BadRequest($this->id . ' fetchOrders() does not support $marginMode ' . $marginMode . ' for spot-margin trading');
                    }
                    $response = Async\await($this->spotPrivateGetMarginAllOrders ($this->extend($request, $queryInner)));
                } else {
                    $response = Async\await($this->spotPrivateGetAllOrders ($this->extend($request, $queryInner)));
                }
                //
                // spot
                //
                //     array(
                //         array(
                //             "symbol" => "BTCUSDT",
                //             "orderId" => "133949373632483328",
                //             "orderListId" => "-1",
                //             "clientOrderId" => null,
                //             "price" => "45000",
                //             "origQty" => "0.0002",
                //             "executedQty" => "0",
                //             "cummulativeQuoteQty" => "0",
                //             "status" => "NEW",
                //             "timeInForce" => null,
                //             "type" => "LIMIT",
                //             "side" => "SELL",
                //             "stopPrice" => null,
                //             "icebergQty" => null,
                //             "time" => "1647718255000",
                //             "updateTime" => "1647718255000",
                //             "isWorking" => true,
                //             "origQuoteOrderQty" => "9"
                //         ),
                //     )
                //
                // margin
                //
                //     array(
                //         {
                //             "symbol" => "BTCUSDT",
                //             "orderId" => "763307297891028992",
                //             "orderListId" => "-1",
                //             "clientOrderId" => null,
                //             "price" => "18000",
                //             "origQty" => "0.0014",
                //             "executedQty" => "0",
                //             "cummulativeQuoteQty" => "0",
                //             "status" => "NEW",
                //             "type" => "LIMIT",
                //             "side" => "BUY",
                //             "isIsolated" => true,
                //             "isWorking" => true,
                //             "time" => 1662153107000,
                //             "updateTime" => 1662153107000
                //         }
                //     )
                //
                return $this->parse_orders($response, $market, $since, $limit);
            } else {
                if ($since !== null) {
                    $request['start_time'] = $since;
                    $end = $this->safe_integer($params, 'end_time', $until);
                    if ($end === null) {
                        $request['end_time'] = $this->sum($since, $this->options['maxTimeTillEnd']);
                    } else {
                        if (($end - $since) > $this->options['maxTimeTillEnd']) {
                            throw new BadRequest($this->id . ' $end is invalid, i.e. exceeds allowed 90 days.');
                        } else {
                            $request['end_time'] = $until;
                        }
                    }
                } elseif ($until !== null) {
                    $request['start_time'] = $this->sum($until, $this->options['maxTimeTillEnd'] * -1);
                    $request['end_time'] = $until;
                }
                if ($limit !== null) {
                    $request['page_size'] = $limit;
                }
                $method = $this->safe_string($this->options, 'fetchOrders', 'contractPrivateGetOrderListHistoryOrders');
                $method = $this->safe_string($query, 'method', $method);
                $ordersOfRegular = array();
                $ordersOfTrigger = array();
                if ($method === 'contractPrivateGetOrderListHistoryOrders') {
                    $response = Async\await($this->contractPrivateGetOrderListHistoryOrders ($this->extend($request, $query)));
                    //
                    //     {
                    //         "success" => true,
                    //         "code" => "0",
                    //         "data" => array(
                    //             array(
                    //                 "orderId" => "265230764677709315",
                    //                 "symbol" => "STEPN_USDT",
                    //                 "positionId" => "0",
                    //                 "price" => "2.1",
                    //                 "vol" => "102",
                    //                 "leverage" => "20",
                    //                 "side" => "1",
                    //                 "category" => "1",
                    //                 "orderType" => "1",
                    //                 "dealAvgPrice" => "0",
                    //                 "dealVol" => "0",
                    //                 "orderMargin" => "10.96704",
                    //                 "takerFee" => "0",
                    //                 "makerFee" => "0",
                    //                 "profit" => "0",
                    //                 "feeCurrency" => "USDT",
                    //                 "openType" => "1",
                    //                 "state" => "2",
                    //                 "externalOid" => "_m_7e42f8df6b324c869e4e200397e2b00f",
                    //                 "errorCode" => "0",
                    //                 "usedMargin" => "0",
                    //                 "createTime" => "1648906342000",
                    //                 "updateTime" => "1648906342000",
                    //                 "positionMode" => "1"
                    //             ),
                    //          )
                    //     }
                    //
                    $ordersOfRegular = $this->safe_value($response, 'data');
                } else {
                    // the Planorder endpoints work not only for stop-$market orders, but also for stop-$limit orders that were supposed to have a separate endpoint
                    $response = Async\await($this->contractPrivateGetPlanorderListOrders ($this->extend($request, $query)));
                    //
                    //     {
                    //         "success" => true,
                    //         "code" => "0",
                    //         "data" => array(
                    //             array(
                    //                 "symbol" => "STEPN_USDT",
                    //                 "leverage" => "20",
                    //                 "side" => "1",
                    //                 "vol" => "13",
                    //                 "openType" => "1",
                    //                 "state" => "1",
                    //                 "orderType" => "1",
                    //                 "errorCode" => "0",
                    //                 "createTime" => "1648984276000",
                    //                 "updateTime" => "1648984276000",
                    //                 "id" => "265557643326564352",
                    //                 "triggerType" => "1",
                    //                 "triggerPrice" => "3",
                    //                 "price" => "2.9", // not present in stop-$market, but in stop-$limit order
                    //                 "executeCycle" => "87600",
                    //                 "trend" => "1",
                    //             ),
                    //         )
                    //     }
                    //
                    $ordersOfTrigger = $this->safe_value($response, 'data');
                }
                $merged = $this->array_concat($ordersOfTrigger, $ordersOfRegular);
                return $this->parse_orders($merged, $market, $since, $limit, $params);
            }
        }) ();
    }

    public function fetch_orders_by_ids($ids, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($ids, $symbol, $params) {
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            list($marketType, $query) = $this->handle_market_type_and_params('fetchOrdersByIds', $market, $params);
            if ($marketType === 'spot') {
                throw new BadRequest($this->id . ' fetchOrdersByIds() is not supported for ' . $marketType);
            } else {
                $request['order_ids'] = implode(',', $ids);
                $response = Async\await($this->contractPrivateGetOrderBatchQuery ($this->extend($request, $query)));
                //
                //     {
                //         "success" => true,
                //         "code" => "0",
                //         "data" => array(
                //             {
                //                 "orderId" => "265230764677709315",
                //                 "symbol" => "STEPN_USDT",
                //                 "positionId" => "0",
                //                 "price" => "2.1",
                //                 "vol" => "102",
                //                 "leverage" => "20",
                //                 "side" => "1",
                //                 "category" => "1",
                //                 "orderType" => "1",
                //                 "dealAvgPrice" => "0",
                //                 "dealVol" => "0",
                //                 "orderMargin" => "10.96704",
                //                 "takerFee" => "0",
                //                 "makerFee" => "0",
                //                 "profit" => "0",
                //                 "feeCurrency" => "USDT",
                //                 "openType" => "1",
                //                 "state" => "2",
                //                 "externalOid" => "_m_7e42f8df6b324c869e4e200397e2b00f",
                //                 "errorCode" => "0",
                //                 "usedMargin" => "0",
                //                 "createTime" => "1648906342000",
                //                 "updateTime" => "1648906342000",
                //                 "positionMode" => "1"
                //             }
                //         )
                //     }
                //
                $data = $this->safe_list($response, 'data');
                return $this->parse_orders($data, $market);
            }
        }) ();
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch all unfilled currently open orders
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#current-open-orders
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-of-the-user-39-s-historical-orders
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#gets-the-trigger-order-list
             *
             * @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
             * @param {string} [$params->marginMode] only 'isolated' is supported, for spot-margin trading
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            $marketType = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            list($marketType, $params) = $this->handle_market_type_and_params('fetchOpenOrders', $market, $params);
            if ($marketType === 'spot') {
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' fetchOpenOrders() requires a $symbol argument for spot market');
                }
                $request['symbol'] = $market['id'];
                list($marginMode, $query) = $this->handle_margin_mode_and_params('fetchOpenOrders', $params);
                $response = null;
                if ($marginMode !== null) {
                    if ($marginMode !== 'isolated') {
                        throw new BadRequest($this->id . ' fetchOpenOrders() does not support $marginMode ' . $marginMode . ' for spot-margin trading');
                    }
                    $response = Async\await($this->spotPrivateGetMarginOpenOrders ($this->extend($request, $query)));
                } else {
                    $response = Async\await($this->spotPrivateGetOpenOrders ($this->extend($request, $query)));
                }
                //
                // spot
                //
                //     array(
                //         {
                //             "symbol" => "BTCUSDT",
                //             "orderId" => "133949373632483328",
                //             "orderListId" => "-1",
                //             "clientOrderId" => "",
                //             "price" => "45000",
                //             "origQty" => "0.0002",
                //             "executedQty" => "0",
                //             "cummulativeQuoteQty" => "0",
                //             "status" => "NEW",
                //             "timeInForce" => null,
                //             "type" => "LIMIT",
                //             "side" => "SELL",
                //             "stopPrice" => null,
                //             "icebergQty" => null,
                //             "time" => "1647718255199",
                //             "updateTime" => null,
                //             "isWorking" => true,
                //             "origQuoteOrderQty" => "9"
                //         }
                //     )
                //
                // margin
                //
                //     array(
                //         {
                //             "symbol" => "BTCUSDT",
                //             "orderId" => "764547676405633024",
                //             "orderListId" => "-1",
                //             "clientOrderId" => null,
                //             "price" => "18000",
                //             "origQty" => "0.0013",
                //             "executedQty" => "0",
                //             "cummulativeQuoteQty" => "0",
                //             "status" => "NEW",
                //             "type" => "LIMIT",
                //             "side" => "BUY",
                //             "isIsolated" => true,
                //             "isWorking" => true,
                //             "time" => 1662448836000,
                //             "updateTime" => 1662448836000
                //         }
                //     )
                //
                return $this->parse_orders($response, $market, $since, $limit);
            } else {
                // TO_DO => another possible way is through => open_orders/{$symbol}, but have same ratelimits, and less granularity, i think historical orders are more convenient, supports more $params (however, theoretically, open-orders endpoint might be sligthly fast)
                return Async\await($this->fetch_orders_by_state(2, $symbol, $since, $limit, $params));
            }
        }) ();
    }

    public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple closed orders made by the user
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#all-orders
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-of-the-user-39-s-historical-orders
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#gets-the-trigger-order-list
             *
             * @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~
             */
            return Async\await($this->fetch_orders_by_state(3, $symbol, $since, $limit, $params));
        }) ();
    }

    public function fetch_canceled_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple canceled orders made by the user
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#all-orders
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-of-the-user-39-s-historical-orders
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#gets-the-trigger-order-list
             *
             * @param {string} $symbol unified market $symbol of the market orders were made in
             * @param {int} [$since] timestamp in ms of the earliest order, default is null
             * @param {int} [$limit] max number of orders to return, default is 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~
             */
            return Async\await($this->fetch_orders_by_state(4, $symbol, $since, $limit, $params));
        }) ();
    }

    public function fetch_orders_by_state($state, ?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($state, $symbol, $since, $limit, $params) {
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            list($marketType) = $this->handle_market_type_and_params('fetchOrdersByState', $market, $params);
            if ($marketType === 'spot') {
                throw new NotSupported($this->id . ' fetchOrdersByState() is not supported for ' . $marketType);
            } else {
                $request['states'] = $state;
                return Async\await($this->fetch_orders($symbol, $since, $limit, $this->extend($request, $params)));
            }
        }) ();
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             * cancels an open $order
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#cancel-$order
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#cancel-the-$order-under-maintenance
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#cancel-the-stop-limit-trigger-$order-under-maintenance
             *
             * @param {string} $id $order $id
             * @param {string} $symbol unified $symbol of the $market the $order was made in
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->marginMode] only 'isolated' is supported for spot-margin trading
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $marketType = null;
            list($marketType, $params) = $this->handle_market_type_and_params('cancelOrder', $market, $params);
            list($marginMode, $query) = $this->handle_margin_mode_and_params('cancelOrder', $params);
            $data = null;
            if ($marketType === 'spot') {
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' cancelOrder() requires a $symbol argument');
                }
                $requestInner = array(
                    'symbol' => $market['id'],
                );
                $clientOrderId = $this->safe_string($params, 'clientOrderId');
                if ($clientOrderId !== null) {
                    $params = $this->omit($query, 'clientOrderId');
                    $requestInner['origClientOrderId'] = $clientOrderId;
                } else {
                    $requestInner['orderId'] = $id;
                }
                if ($marginMode !== null) {
                    if ($marginMode !== 'isolated') {
                        throw new BadRequest($this->id . ' cancelOrder() does not support $marginMode ' . $marginMode . ' for spot-margin trading');
                    }
                    $data = Async\await($this->spotPrivateDeleteMarginOrder ($this->extend($requestInner, $query)));
                } else {
                    $data = Async\await($this->spotPrivateDeleteOrder ($this->extend($requestInner, $query)));
                }
                //
                // spot
                //
                //     {
                //         "symbol" => "BTCUSDT",
                //         "orderId" => "133734823834447872",
                //         "price" => "30000",
                //         "origQty" => "0.0002",
                //         "type" => "LIMIT",
                //         "side" => "BUY"
                //     }
                //
                // margin
                //
                //     array(
                //         {
                //             "symbol" => "BTCUSDT",
                //             "orderId" => "762640232574226432",
                //             "orderListId" => "-1",
                //             "clientOrderId" => null,
                //             "price" => "18000",
                //             "origQty" => "0.00147",
                //             "executedQty" => "0",
                //             "cummulativeQuoteQty" => "0",
                //             "status" => "NEW",
                //             "type" => "LIMIT",
                //             "side" => "BUY",
                //             "isIsolated" => true,
                //             "isWorking" => true,
                //             "time" => 1661994066000,
                //             "updateTime" => 1661994066000
                //         }
                //     )
                //
            } else {
                // TODO => PlanorderCancel endpoint has bug atm. waiting for fix.
                $method = $this->safe_string($this->options, 'cancelOrder', 'contractPrivatePostOrderCancel'); // contractPrivatePostOrderCancel, contractPrivatePostPlanorderCancel
                $method = $this->safe_string($query, 'method', $method);
                $response = null;
                if ($method === 'contractPrivatePostOrderCancel') {
                    $response = Async\await($this->contractPrivatePostOrderCancel (array( $id ))); // the $request cannot be changed or extended. This is the only way to send.
                } elseif ($method === 'contractPrivatePostPlanorderCancel') {
                    $response = Async\await($this->contractPrivatePostPlanorderCancel (array( $id ))); // the $request cannot be changed or extended. This is the only way to send.
                } else {
                    throw new NotSupported($this->id . ' cancelOrder() not support this method');
                }
                //
                //     {
                //         "success" => true,
                //         "code" => "0",
                //         "data" => array(
                //             {
                //                 "orderId" => "264995729269765120",
                //                 "errorCode" => "0",         // if already canceled => "2041"; if doesn't exist => "2040"
                //                 "errorMsg" => "success",    // if already canceled => "order state cannot be cancelled"; if doesn't exist => "order not exist"
                //             }
                //         )
                //     }
                //
                $data = $this->safe_value($response, 'data');
                $order = $this->safe_value($data, 0);
                $errorMsg = $this->safe_value($order, 'errorMsg', '');
                if ($errorMsg !== 'success') {
                    throw new InvalidOrder($this->id . ' cancelOrder() the $order with $id ' . $id . ' cannot be cancelled => ' . $errorMsg);
                }
            }
            return $this->parse_order($data, $market);
        }) ();
    }

    public function cancel_orders($ids, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($ids, $symbol, $params) {
            /**
             * cancel multiple orders
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#cancel-the-order-under-maintenance
             *
             * @param {string[]} $ids order $ids
             * @param {string} $symbol unified $market $symbol, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $market = ($symbol !== null) ? $this->market($symbol) : null;
            list($marketType) = $this->handle_market_type_and_params('cancelOrders', $market, $params);
            if ($marketType === 'spot') {
                throw new BadRequest($this->id . ' cancelOrders() is not supported for ' . $marketType);
            } else {
                $response = Async\await($this->contractPrivatePostOrderCancel ($ids)); // the request cannot be changed or extended. The only way to send.
                //
                //     {
                //         "success" => true,
                //         "code" => "0",
                //         "data" => array(
                //             array(
                //                 "orderId" => "264995729269765120",
                //                 "errorCode" => "0",         // if already canceled => "2041"
                //                 "errorMsg" => "success",    // if already canceled => "order state cannot be cancelled"
                //             ),
                //         )
                //     }
                //
                $data = $this->safe_list($response, 'data');
                return $this->parse_orders($data, $market);
            }
        }) ();
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * cancel all open orders
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#cancel-all-open-orders-on-a-$symbol
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#cancel-all-orders-under-a-contract-under-maintenance
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#cancel-all-trigger-orders-under-maintenance
             *
             * @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
             * @param {string} [$params->marginMode] only 'isolated' is supported for spot-margin trading
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $market = ($symbol !== null) ? $this->market($symbol) : null;
            $request = array();
            $marketType = null;
            list($marketType, $params) = $this->handle_market_type_and_params('cancelAllOrders', $market, $params);
            list($marginMode, $query) = $this->handle_margin_mode_and_params('cancelAllOrders', $params);
            if ($marketType === 'spot') {
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' cancelAllOrders() requires a $symbol argument on spot');
                }
                $request['symbol'] = $market['id'];
                $response = null;
                if ($marginMode !== null) {
                    if ($marginMode !== 'isolated') {
                        throw new BadRequest($this->id . ' cancelAllOrders() does not support $marginMode ' . $marginMode . ' for spot-margin trading');
                    }
                    $response = Async\await($this->spotPrivateDeleteMarginOpenOrders ($this->extend($request, $query)));
                } else {
                    $response = Async\await($this->spotPrivateDeleteOpenOrders ($this->extend($request, $query)));
                }
                //
                // spot
                //
                //     array(
                //         array(
                //             "symbol" => "BTCUSDT",
                //             "orderId" => "133926492139692032",
                //             "price" => "30000",
                //             "origQty" => "0.0002",
                //             "type" => "LIMIT",
                //             "side" => "BUY"
                //         ),
                //     )
                //
                // margin
                //
                //     array(
                //         {
                //             "symbol" => "BTCUSDT",
                //             "orderId" => "762640232574226432",
                //             "orderListId" => "-1",
                //             "clientOrderId" => null,
                //             "price" => "18000",
                //             "origQty" => "0.00147",
                //             "executedQty" => "0",
                //             "cummulativeQuoteQty" => "0",
                //             "status" => "NEW",
                //             "type" => "LIMIT",
                //             "side" => "BUY",
                //             "isIsolated" => true,
                //             "isWorking" => true,
                //             "time" => 1661994066000,
                //             "updateTime" => 1661994066000
                //         }
                //     )
                //
                return $this->parse_orders($response, $market);
            } else {
                if ($symbol !== null) {
                    $request['symbol'] = $market['id'];
                }
                // $method can be either => contractPrivatePostOrderCancelAll or contractPrivatePostPlanorderCancelAll
                // the Planorder endpoints work not only for stop-$market orders but also for stop-limit orders that are supposed to have separate endpoint
                $method = $this->safe_string($this->options, 'cancelAllOrders', 'contractPrivatePostOrderCancelAll');
                $method = $this->safe_string($query, 'method', $method);
                $response = null;
                if ($method === 'contractPrivatePostOrderCancelAll') {
                    $response = Async\await($this->contractPrivatePostOrderCancelAll ($this->extend($request, $query)));
                } elseif ($method === 'contractPrivatePostPlanorderCancelAll') {
                    $response = Async\await($this->contractPrivatePostPlanorderCancelAll ($this->extend($request, $query)));
                }
                //
                //     {
                //         "success" => true,
                //         "code" => "0"
                //     }
                //
                $data = $this->safe_list($response, 'data', array());
                return $this->parse_orders($data, $market);
            }
        }) ();
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        // spot
        //    createOrder
        //
        //    {
        //        "symbol" => "FARTCOINUSDT",
        //        "orderId" => "C02__342252993005723644225",
        //        "orderListId" => "-1",
        //        "price" => "1.1",
        //        "origQty" => "6.3",
        //        "type" => "IMMEDIATE_OR_CANCEL",
        //        "side" => "SELL",
        //        "transactTime" => "1745852205223"
        //    }
        //
        //    unknown endpoint on spot
        //
        //    {
        //         "symbol" => "BTCUSDT",
        //         "orderId" => "123738410679123456",
        //         "orderListId" => -1
        //    }
        //
        // margin => createOrder
        //
        //     {
        //         "symbol" => "BTCUSDT",
        //         "orderId" => "762634301354414080",
        //         "clientOrderId" => null,
        //         "isIsolated" => true,
        //         "transactTime" => 1661992652132
        //     }
        //
        // spot => cancelOrder, cancelAllOrders
        //
        //     {
        //         "symbol" => "BTCUSDT",
        //         "orderId" => "133926441921286144",
        //         "price" => "30000",
        //         "origQty" => "0.0002",
        //         "type" => "LIMIT",
        //         "side" => "BUY"
        //     }
        //
        // margin => cancelOrder, cancelAllOrders
        //
        //     {
        //         "symbol" => "BTCUSDT",
        //         "orderId" => "762640232574226432",
        //         "orderListId" => "-1",
        //         "clientOrderId" => null,
        //         "price" => "18000",
        //         "origQty" => "0.00147",
        //         "executedQty" => "0",
        //         "cummulativeQuoteQty" => "0",
        //         "status" => "NEW",
        //         "type" => "LIMIT",
        //         "side" => "BUY",
        //         "isIsolated" => true,
        //         "isWorking" => true,
        //         "time" => 1661994066000,
        //         "updateTime" => 1661994066000
        //     }
        //
        // spot => fetchOrder, fetchOpenOrders, fetchOrders
        //
        //     {
        //         "symbol" => "BTCUSDT",
        //         "orderId" => "133734823834147272",
        //         "orderListId" => "-1",
        //         "clientOrderId" => null,
        //         "price" => "30000",
        //         "origQty" => "0.0002",
        //         "executedQty" => "0",
        //         "cummulativeQuoteQty" => "0",
        //         "status" => "CANCELED",
        //         "timeInForce" => null,
        //         "type" => "LIMIT",
        //         "side" => "BUY",
        //         "stopPrice" => null,
        //         "icebergQty" => null,
        //         "time" => "1647667102000",
        //         "updateTime" => "1647708567000",
        //         "isWorking" => true,
        //         "origQuoteOrderQty" => "6"
        //     }
        //
        // margin => fetchOrder, fetchOrders
        //
        //     {
        //         "symbol" => "BTCUSDT",
        //         "orderId" => "763307297891028992",
        //         "orderListId" => "-1",
        //         "clientOrderId" => null,
        //         "price" => "18000",
        //         "origQty" => "0.0014",
        //         "executedQty" => "0",
        //         "cummulativeQuoteQty" => "0",
        //         "status" => "NEW",
        //         "type" => "LIMIT",
        //         "side" => "BUY",
        //         "isIsolated" => true,
        //         "isWorking" => true,
        //         "time" => 1662153107000,
        //         "updateTime" => 1662153107000
        //     }
        //
        // swap => createOrder
        //
        //     2ff3163e8617443cb9c6fc19d42b1ca4
        //
        // swap => fetchOrder, fetchOrders
        //
        //     regular
        //     {
        //         "orderId" => "264995729269765120",
        //         "symbol" => "STEPN_USDT",
        //         "positionId" => "0",
        //         "price" => "2.2",
        //         "vol" => "15",
        //         "leverage" => "20",
        //         "side" => "1", // TODO => not unified
        //         "category" => "1",
        //         "orderType" => "1", // TODO => not unified
        //         "dealAvgPrice" => "0",
        //         "dealVol" => "0",
        //         "orderMargin" => "2.2528",
        //         "takerFee" => "0",
        //         "makerFee" => "0",
        //         "profit" => "0",
        //         "feeCurrency" => "USDT",
        //         "openType" => "1",
        //         "state" => "2", // TODO
        //         "externalOid" => "_m_0e9520c256744d64b942985189026d20",
        //         "errorCode" => "0",
        //         "usedMargin" => "0",
        //         "createTime" => "1648850305236",
        //         "updateTime" => "1648850305245",
        //         "positionMode" => "1"
        //     }
        //
        //     stop
        //     {
        //         "id" => "265557643326564352",
        //         "triggerType" => "1",
        //         "triggerPrice" => "3",
        //         "price" => "2.9", // not present in stop-$market, but in stop-limit $order
        //         "executeCycle" => "87600",
        //         "trend" => "1",
        //          // below keys are same regular $order structure
        //         "symbol" => "STEPN_USDT",
        //         "leverage" => "20",
        //         "side" => "1",
        //         "vol" => "13",
        //         "openType" => "1",
        //         "state" => "1",
        //         "orderType" => "1",
        //         "errorCode" => "0",
        //         "createTime" => "1648984276000",
        //         "updateTime" => "1648984276000",
        //     }
        //
        // createOrders error
        //
        //     {
        //         "newClientOrderId" => "123456",
        //         "msg" => "The minimum transaction volume cannot be less than:0.5USDT",
        //         "code" => 30002
        //     }
        //
        $code = $this->safe_integer($order, 'code');
        if ($code !== null) {
            // error upon placing multiple orders
            return $this->safe_order(array(
                'info' => $order,
                'status' => 'rejected',
                'clientOrderId' => $this->safe_string($order, 'newClientOrderId'),
            ));
        }
        $id = null;
        if (gettype($order) === 'string') {
            $id = $order;
        } else {
            $id = $this->safe_string_2($order, 'orderId', 'id');
        }
        $timeInForce = $this->parse_order_time_in_force($this->safe_string($order, 'timeInForce'));
        $typeRaw = $this->safe_string($order, 'type');
        if ($timeInForce === null) {
            $timeInForce = $this->get_tif_from_raw_order_type($typeRaw);
        }
        $marketId = $this->safe_string($order, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $timestamp = $this->safe_integer_n($order, array( 'time', 'createTime', 'transactTime' ));
        $fee = null;
        $feeCurrency = $this->safe_string($order, 'feeCurrency');
        if ($feeCurrency !== null) {
            $takerFee = $this->safe_string($order, 'takerFee');
            $makerFee = $this->safe_string($order, 'makerFee');
            $feeSum = Precise::string_add($takerFee, $makerFee);
            $fee = array(
                'currency' => $feeCurrency,
                'cost' => $this->parse_number($feeSum),
            );
        }
        return $this->safe_order(array(
            'id' => $id,
            'clientOrderId' => $this->safe_string($order, 'clientOrderId'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => null, // TODO => this might be 'updateTime' if $order-status is filled, otherwise cancellation time. needs to be checked
            'status' => $this->parse_order_status($this->safe_string_2($order, 'status', 'state')),
            'symbol' => $market['symbol'],
            'type' => $this->parse_order_type($typeRaw),
            'timeInForce' => $timeInForce,
            'side' => $this->parse_order_side($this->safe_string($order, 'side')),
            'price' => $this->safe_number($order, 'price'),
            'triggerPrice' => $this->safe_number_2($order, 'stopPrice', 'triggerPrice'),
            'average' => $this->safe_number($order, 'dealAvgPrice'),
            'amount' => $this->safe_number_2($order, 'origQty', 'vol'),
            'cost' => $this->safe_number($order, 'cummulativeQuoteQty'),  // 'cummulativeQuoteQty' vs 'origQuoteOrderQty'
            'filled' => $this->safe_number_2($order, 'executedQty', 'dealVol'),
            'remaining' => null,
            'fee' => $fee,
            'trades' => null,
            'info' => $order,
        ), $market);
    }

    public function parse_order_side($status) {
        $statuses = array(
            'BUY' => 'buy',
            'SELL' => 'sell',
            '1' => 'buy',
            '2' => 'sell',
            // contracts v1 : TODO
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_order_type($status) {
        $statuses = array(
            'MARKET' => 'market',
            'LIMIT' => 'limit',
            'LIMIT_MAKER' => 'limit',
            // on spot, during submission below types are used only accepted order
            'IMMEDIATE_OR_CANCEL' => 'limit',
            'FILL_OR_KILL' => 'limit',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_order_status(?string $status) {
        $statuses = array(
            'NEW' => 'open',
            'FILLED' => 'closed',
            'CANCELED' => 'canceled',
            'PARTIALLY_FILLED' => 'open',
            'PARTIALLY_CANCELED' => 'canceled',
            // contracts v1
            // '1' => 'uninformed', // TODO => wt?
            '2' => 'open',
            '3' => 'closed',
            '4' => 'canceled',
            // '5' => 'invalid', //  TODO => wt?
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function parse_order_time_in_force($status) {
        $statuses = array(
            'GTC' => 'GTC',
            'FOK' => 'FOK',
            'IOC' => 'IOC',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function get_tif_from_raw_order_type(?string $orderType = null) {
        $statuses = array(
            'LIMIT' => 'GTC',
            'LIMIT_MAKER' => 'POST_ONLY',
            'IMMEDIATE_OR_CANCEL' => 'IOC',
            'FILL_OR_KILL' => 'FOK',
            'MARKET' => 'IOC',
        );
        return $this->safe_string($statuses, $orderType, $orderType);
    }

    public function fetch_account_helper($type, $params) {
        return Async\async(function () use ($type, $params) {
            if ($type === 'spot') {
                return Async\await($this->spotPrivateGetAccount ($params));
                //
                //     {
                //         "makerCommission" => "20",
                //         "takerCommission" => "20",
                //         "buyerCommission" => "0",
                //         "sellerCommission" => "0",
                //         "canTrade" => true,
                //         "canWithdraw" => true,
                //         "canDeposit" => true,
                //         "updateTime" => null,
                //         "accountType" => "SPOT",
                //         "balances" => array(
                //             array(
                //                 "asset" => "BTC",
                //                 "free" => "0.002",
                //                 "locked" => "0"
                //             ),
                //             array(
                //                 "asset" => "USDT",
                //                 "free" => "88.120131350620957006",
                //                 "locked" => "0"
                //             ),
                //         ),
                //         "permissions" => array(
                //             "SPOT"
                //         )
                //     }
                //
            } elseif ($type === 'swap') {
                $response = Async\await($this->contractPrivateGetAccountAssets ($params));
                //
                //     {
                //         "success":true,
                //         "code":0,
                //         "data":array(
                //            array(
                //              "currency":"BSV",
                //              "positionMargin":0,
                //              "availableBalance":0,
                //              "cashBalance":0,
                //              "frozenBalance":0,
                //              "equity":0,
                //              "unrealized":0,
                //              "bonus":0
                //           ),
                //         )
                //     }
                //
                return $this->safe_value($response, 'data');
            }
            return null;
        }) ();
    }

    public function fetch_accounts($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetch all the accounts associated with a profile
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#$account-information
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-informations-of-user-39-s-asset
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=$account-structure $account structures~ indexed by the $account type
             */
            // TODO => is the below endpoints suitable for fetchAccounts?
            list($marketType, $query) = $this->handle_market_type_and_params('fetchAccounts', null, $params);
            Async\await($this->load_markets());
            $response = Async\await($this->fetch_account_helper($marketType, $query));
            $data = $this->safe_value($response, 'balances', array());
            $result = array();
            for ($i = 0; $i < count($data); $i++) {
                $account = $data[$i];
                $currencyId = $this->safe_string_2($account, 'asset', 'currency');
                $code = $this->safe_currency_code($currencyId);
                $result[] = array(
                    'id' => $this->safe_string($account, 'id'),
                    'type' => $this->safe_string($account, 'type'),
                    'code' => $code,
                    'info' => $account,
                );
            }
            return $result;
        }) ();
    }

    public function fetch_trading_fee(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the trading fees for a $market
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#query-mx-deduct-status
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=fee-structure fee structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if (!$market['spot']) {
                throw new BadRequest($this->id . ' fetchTradingFee() supports spot markets only');
            }
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->spotPrivateGetTradeFee ($this->extend($request, $params)));
            //
            //  {
            //      "data":array(
            //        "makerCommission":0.003000000000000000,
            //        "takerCommission":0.003000000000000000
            //      ),
            //      "code":0,
            //      "msg":"success",
            //      "timestamp":1669109672717
            //  }
            //
            $data = $this->safe_dict($response, 'data', array());
            return array(
                'info' => $data,
                'symbol' => $symbol,
                'maker' => $this->safe_number($data, 'makerCommission'),
                'taker' => $this->safe_number($data, 'takerCommission'),
                'percentage' => null,
                'tierBased' => null,
            );
        }) ();
    }

    public function custom_parse_balance($response, $marketType): array {
        //
        // spot
        //
        //     {
        //         "asset" => "USDT",
        //         "free" => "0.000000000674",
        //         "locked" => "0"
        //     }
        //
        // swap
        //
        //     {
        //         "currency" => "BSV",
        //         "positionMargin" => 0,
        //         "availableBalance" => 0,
        //         "cashBalance" => 0,
        //         "frozenBalance" => 0,
        //         "equity" => 0,
        //         "unrealized" => 0,
        //         "bonus" => 0
        //     }
        //
        // margin
        //
        //     {
        //         "baseAsset" => {
        //             "asset" => "BTC",
        //             "borrowEnabled" => true,
        //             "borrowed" => "0",
        //             "free" => "0",
        //             "interest" => "0",
        //             "locked" => "0",
        //             "netAsset" => "0",
        //             "netAssetOfBtc" => "0",
        //             "repayEnabled" => true,
        //             "totalAsset" => "0"
        //         }
        //         "quoteAsset" => {
        //             "asset" => "USDT",
        //             "borrowEnabled" => true,
        //             "borrowed" => "0",
        //             "free" => "10",
        //             "interest" => "0",
        //             "locked" => "0",
        //             "netAsset" => "10",
        //             "netAssetOfBtc" => "0",
        //             "repayEnabled" => true,
        //             "totalAsset" => "10"
        //         }
        //         "symbol" => "BTCUSDT",
        //         "isolatedCreated" => true,
        //         "enabled" => true,
        //         "marginLevel" => "999",
        //         "marginRatio" => "9",
        //         "indexPrice" => "16741.137068965517241379",
        //         "liquidatePrice" => "--",
        //         "liquidateRate" => "--",
        //         "tradeEnabled" => true
        //     }
        //
        $wallet = null;
        if ($marketType === 'margin') {
            $wallet = $this->safe_value($response, 'assets', array());
        } elseif ($marketType === 'swap') {
            $wallet = $this->safe_value($response, 'data', array());
        } else {
            $wallet = $this->safe_value($response, 'balances', array());
        }
        $result = array( 'info' => $response );
        if ($marketType === 'margin') {
            for ($i = 0; $i < count($wallet); $i++) {
                $entry = $wallet[$i];
                $marketId = $this->safe_string($entry, 'symbol');
                $symbol = $this->safe_symbol($marketId, null);
                $base = $this->safe_value($entry, 'baseAsset', array());
                $quote = $this->safe_value($entry, 'quoteAsset', array());
                $baseCode = $this->safe_currency_code($this->safe_string($base, 'asset'));
                $quoteCode = $this->safe_currency_code($this->safe_string($quote, 'asset'));
                $subResult = array();
                $subResult[$baseCode] = $this->parse_balance_helper($base);
                $subResult[$quoteCode] = $this->parse_balance_helper($quote);
                $result[$symbol] = $this->safe_balance($subResult);
            }
            return $result;
        } elseif ($marketType === 'swap') {
            for ($i = 0; $i < count($wallet); $i++) {
                $entry = $wallet[$i];
                $currencyId = $this->safe_string($entry, 'currency');
                $code = $this->safe_currency_code($currencyId);
                $account = $this->account();
                $account['free'] = $this->safe_string($entry, 'availableBalance');
                $account['used'] = $this->safe_string($entry, 'frozenBalance');
                $result[$code] = $account;
            }
            return $this->safe_balance($result);
        } else {
            for ($i = 0; $i < count($wallet); $i++) {
                $entry = $wallet[$i];
                $currencyId = $this->safe_string($entry, 'asset');
                $code = $this->safe_currency_code($currencyId);
                $account = $this->account();
                $account['free'] = $this->safe_string($entry, 'free');
                $account['used'] = $this->safe_string($entry, 'locked');
                $result[$code] = $account;
            }
            return $this->safe_balance($result);
        }
    }

    public function parse_balance_helper($entry) {
        $account = $this->account();
        $account['used'] = $this->safe_string($entry, 'locked');
        $account['free'] = $this->safe_string($entry, 'free');
        $account['total'] = $this->safe_string($entry, 'totalAsset');
        $debt = $this->safe_string($entry, 'borrowed');
        $interest = $this->safe_string($entry, 'interest');
        $account['debt'] = Precise::string_add($debt, $interest);
        return $account;
    }

    public function fetch_balance($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * query for balance and get the amount of funds available for trading or funds locked in orders
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#account-information
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-informations-of-user-39-s-asset
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#isolated-account
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->symbols] // required for margin, $market id's separated by commas
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
             */
            Async\await($this->load_markets());
            $marketType = null;
            $request = array();
            list($marketType, $params) = $this->handle_market_type_and_params('fetchBalance', null, $params);
            $marginMode = $this->safe_string($params, 'marginMode');
            $isMargin = $this->safe_bool($params, 'margin', false);
            $params = $this->omit($params, array( 'margin', 'marginMode' ));
            $response = null;
            if (($marginMode !== null) || ($isMargin) || ($marketType === 'margin')) {
                $parsedSymbols = null;
                $symbol = $this->safe_string($params, 'symbol');
                if ($symbol === null) {
                    $symbols = $this->safe_value($params, 'symbols');
                    if ($symbols !== null) {
                        $parsedSymbols = implode(',', $this->market_ids($symbols));
                    }
                } else {
                    $market = $this->market($symbol);
                    $parsedSymbols = $market['id'];
                }
                $this->check_required_argument('fetchBalance', $parsedSymbols, 'symbol or symbols');
                $marketType = 'margin';
                $request['symbols'] = $parsedSymbols;
                $params = $this->omit($params, array( 'symbol', 'symbols' ));
                $response = Async\await($this->spotPrivateGetMarginIsolatedAccount ($this->extend($request, $params)));
            } elseif ($marketType === 'spot') {
                $response = Async\await($this->spotPrivateGetAccount ($this->extend($request, $params)));
            } elseif ($marketType === 'swap') {
                $response = Async\await($this->contractPrivateGetAccountAssets ($this->extend($request, $params)));
            } else {
                throw new NotSupported($this->id . ' fetchBalance() not support this method');
            }
            //
            // spot
            //
            //     {
            //         "makerCommission" => 0,
            //         "takerCommission" => 20,
            //         "buyerCommission" => 0,
            //         "sellerCommission" => 0,
            //         "canTrade" => true,
            //         "canWithdraw" => true,
            //         "canDeposit" => true,
            //         "updateTime" => null,
            //         "accountType" => "SPOT",
            //         "balances" => array(
            //             array(
            //                 "asset" => "USDT",
            //                 "free" => "0.000000000674",
            //                 "locked" => "0"
            //             ),
            //         ),
            //         "permissions" => ["SPOT"]
            //     }
            //
            // swap
            //
            //     {
            //         "success" => true,
            //         "code" => 0,
            //         "data" => array(
            //             array(
            //                 "currency" => "BSV",
            //                 "positionMargin" => 0,
            //                 "availableBalance" => 0,
            //                 "cashBalance" => 0,
            //                 "frozenBalance" => 0,
            //                 "equity" => 0,
            //                 "unrealized" => 0,
            //                 "bonus" => 0
            //             ),
            //         )
            //     }
            //
            // margin
            //
            //     {
            //         "assets" => array(
            //             {
            //                 "baseAsset" => array(
            //                     "asset" => "BTC",
            //                     "borrowEnabled" => true,
            //                     "borrowed" => "0",
            //                     "free" => "0",
            //                     "interest" => "0",
            //                     "locked" => "0",
            //                     "netAsset" => "0",
            //                     "netAssetOfBtc" => "0",
            //                     "repayEnabled" => true,
            //                     "totalAsset" => "0"
            //                 ),
            //                 "quoteAsset" => array(
            //                     "asset" => "USDT",
            //                     "borrowEnabled" => true,
            //                     "borrowed" => "0",
            //                     "free" => "10",
            //                     "interest" => "0",
            //                     "locked" => "0",
            //                     "netAsset" => "10",
            //                     "netAssetOfBtc" => "0",
            //                     "repayEnabled" => true,
            //                     "totalAsset" => "10"
            //                 ),
            //                 "symbol" => "BTCUSDT",
            //                 "isolatedCreated" => true,
            //                 "enabled" => true,
            //                 "marginLevel" => "999",
            //                 "marginRatio" => "9",
            //                 "indexPrice" => "16741.137068965517241379",
            //                 "liquidatePrice" => "--",
            //                 "liquidateRate" => "--",
            //                 "tradeEnabled" => true
            //             }
            //         )
            //     }
            //
            return $this->custom_parse_balance($response, $marketType);
        }) ();
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch all $trades made by the user
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#account-trade-list
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-all-transaction-details-of-the-user-s-order
             *
             * @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
             * @param {int} [$params->until] the latest time in ms to fetch $trades for
             * @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');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $marketType = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchMyTrades', $market, $params);
            $request = array(
                'symbol' => $market['id'],
            );
            $trades = null;
            if ($marketType === 'spot') {
                if ($since !== null) {
                    $request['startTime'] = $since;
                }
                if ($limit !== null) {
                    $request['limit'] = $limit;
                }
                $until = $this->safe_integer($params, 'until');
                if ($until !== null) {
                    $params = $this->omit($params, 'until');
                    $request['endTime'] = $until;
                }
                $trades = Async\await($this->spotPrivateGetMyTrades ($this->extend($request, $params)));
                //
                // spot
                //
                //     array(
                //         {
                //             "symbol" => "BTCUSDT",
                //             "id" => "133948532984922113",
                //             "orderId" => "133948532531949568",
                //             "orderListId" => "-1",
                //             "price" => "41995.51",
                //             "qty" => "0.0002",
                //             "quoteQty" => "8.399102",
                //             "commission" => "0.016798204",
                //             "commissionAsset" => "USDT",
                //             "time" => "1647718055000",
                //             "isBuyer" => true,
                //             "isMaker" => false,
                //             "isBestMatch" => true
                //         }
                //     )
                //
            } else {
                if ($since !== null) {
                    $request['start_time'] = $since;
                    $end = $this->safe_integer($params, 'end_time');
                    if ($end === null) {
                        $request['end_time'] = $this->sum($since, $this->options['maxTimeTillEnd']);
                    }
                }
                if ($limit !== null) {
                    $request['page_size'] = $limit;
                }
                $response = Async\await($this->contractPrivateGetOrderListOrderDeals ($this->extend($request, $params)));
                //
                //     {
                //         "success" => true,
                //         "code" => "0",
                //         "data" => array(
                //             {
                //                 "id" => "299444585",
                //                 "symbol" => "STEPN_USDT",
                //                 "side" => "1",
                //                 "vol" => "1",
                //                 "price" => "2.45455",
                //                 "feeCurrency" => "USDT",
                //                 "fee" => "0.00147273",
                //                 "timestamp" => "1648924557000",
                //                 "profit" => "0",
                //                 "category" => "1",
                //                 "orderId" => "265307163526610432",
                //                 "positionMode" => "1",
                //                 "taker" => true
                //             }
                //         )
                //     }
                //
                $trades = $this->safe_value($response, 'data');
            }
            return $this->parse_trades($trades, $market, $since, $limit);
        }) ();
    }

    public function fetch_order_trades(string $id, ?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $since, $limit, $params) {
            /**
             * fetch all the $trades made from a single order
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#account-trade-list
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#$query-the-order-based-on-the-order-number
             *
             * @param {string} $id order $id
             * @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 to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?$id=trade-structure trade structures~
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            list($marketType, $query) = $this->handle_market_type_and_params('fetchOrderTrades', $market, $params);
            $trades = null;
            if ($marketType === 'spot') {
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' fetchOrderTrades() requires a $symbol argument');
                }
                $request['symbol'] = $market['id'];
                $request['orderId'] = $id;
                $trades = Async\await($this->spotPrivateGetMyTrades ($this->extend($request, $query)));
                //
                // spot
                //
                //     array(
                //         {
                //             "symbol" => "BTCUSDT",
                //             "id" => "133948532984922113",
                //             "orderId" => "133948532531949568",
                //             "orderListId" => "-1",
                //             "price" => "41995.51",
                //             "qty" => "0.0002",
                //             "quoteQty" => "8.399102",
                //             "commission" => "0.016798204",
                //             "commissionAsset" => "USDT",
                //             "time" => "1647718055000",
                //             "isBuyer" => true,
                //             "isMaker" => false,
                //             "isBestMatch" => true
                //         }
                //     )
                //
            } else {
                $request['order_id'] = $id;
                $response = Async\await($this->contractPrivateGetOrderDealDetailsOrderId ($this->extend($request, $query)));
                //
                //     {
                //         "success" => true,
                //         "code" => "0",
                //         "data" => array(
                //             {
                //                 "id" => "299444585",
                //                 "symbol" => "STEPN_USDT",
                //                 "side" => "1",
                //                 "vol" => "1",
                //                 "price" => "2.45455",
                //                 "feeCurrency" => "USDT",
                //                 "fee" => "0.00147273",
                //                 "timestamp" => "1648924557000",
                //                 "profit" => "0",
                //                 "category" => "1",
                //                 "orderId" => "265307163526610432",
                //                 "positionMode" => "1",
                //                 "taker" => true
                //             }
                //         )
                //     }
                //
                $trades = $this->safe_value($response, 'data');
            }
            return $this->parse_trades($trades, $market, $since, $limit, $query);
        }) ();
    }

    public function modify_margin_helper(string $symbol, $amount, $addOrReduce, $params = array ()) {
        return Async\async(function () use ($symbol, $amount, $addOrReduce, $params) {
            $positionId = $this->safe_integer($params, 'positionId');
            if ($positionId === null) {
                throw new ArgumentsRequired($this->id . ' modifyMarginHelper() requires a $positionId parameter');
            }
            Async\await($this->load_markets());
            $request = array(
                'positionId' => $positionId,
                'amount' => $amount,
                'type' => $addOrReduce,
            );
            $response = Async\await($this->contractPrivatePostPositionChangeMargin ($this->extend($request, $params)));
            //
            //     {
            //         "success" => true,
            //         "code" => 0
            //     }
            return $response;
        }) ();
    }

    public function reduce_margin(string $symbol, float $amount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $amount, $params) {
            /**
             * remove margin from a position
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#increase-or-decrease-margin
             *
             * @param {string} $symbol unified market $symbol
             * @param {float} $amount the $amount of margin to remove
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=reduce-margin-structure margin structure~
             */
            return Async\await($this->modify_margin_helper($symbol, $amount, 'SUB', $params));
        }) ();
    }

    public function add_margin(string $symbol, float $amount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $amount, $params) {
            /**
             * add margin
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#increase-or-decrease-margin
             *
             * @param {string} $symbol unified market $symbol
             * @param {float} $amount amount of margin to add
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=add-margin-structure margin structure~
             */
            return Async\await($this->modify_margin_helper($symbol, $amount, 'ADD', $params));
        }) ();
    }

    public function set_leverage(int $leverage, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($leverage, $symbol, $params) {
            /**
             * set the level of $leverage for a $market
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#switch-$leverage
             *
             * @param {float} $leverage the rate of $leverage
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} response from the exchange
             */
            Async\await($this->load_markets());
            $request = array(
                'leverage' => $leverage,
            );
            $positionId = $this->safe_integer($params, 'positionId');
            if ($positionId === null) {
                $openType = $this->safe_number($params, 'openType'); // 1 or 2
                $positionType = $this->safe_number($params, 'positionType'); // 1 or 2
                $market = ($symbol !== null) ? $this->market($symbol) : null;
                if (($openType === null) || ($positionType === null) || ($market === null)) {
                    throw new ArgumentsRequired($this->id . ' setLeverage() requires a $positionId parameter or a $symbol argument with $openType and $positionType parameters, use $openType 1 or 2 for isolated or cross margin respectively, use $positionType 1 or 2 for long or short positions');
                } else {
                    $request['openType'] = $openType;
                    $request['symbol'] = $market['id'];
                    $request['positionType'] = $positionType;
                }
            } else {
                $request['positionId'] = $positionId;
            }
            return Async\await($this->contractPrivatePostPositionChangeLeverage ($this->extend($request, $params)));
        }) ();
    }

    public function fetch_funding_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch the history of funding payments paid and received on this account
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-details-of-user-s-funding-rate
             *
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch funding history for
             * @param {int} [$limit] the maximum number of funding history structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=funding-history-structure funding history structure~
             */
            Async\await($this->load_markets());
            $market = null;
            $request = array(
                // 'symbol' => $market['id'],
                // 'position_id' => positionId,
                // 'page_num' => 1,
                // 'page_size' => $limit, // default 20, max 100
            );
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            if ($limit !== null) {
                $request['page_size'] = $limit;
            }
            $response = Async\await($this->contractPrivateGetPositionFundingRecords ($this->extend($request, $params)));
            //
            //     {
            //         "success" => true,
            //         "code" => 0,
            //         "data" => {
            //             "pageSize" => 20,
            //             "totalCount" => 2,
            //             "totalPage" => 1,
            //             "currentPage" => 1,
            //             "resultList" => array(
            //                 array(
            //                     "id" => 7423910,
            //                     "symbol" => "BTC_USDT",
            //                     "positionType" => 1,
            //                     "positionValue" => 29.30024,
            //                     "funding" => 0.00076180624,
            //                     "rate" => -0.000026,
            //                     "settleTime" => 1643299200000
            //                 ),
            //                 {
            //                     "id" => 7416473,
            //                     "symbol" => "BTC_USDT",
            //                     "positionType" => 1,
            //                     "positionValue" => 28.9188,
            //                     "funding" => 0.0014748588,
            //                     "rate" => -0.000051,
            //                     "settleTime" => 1643270400000
            //                 }
            //             )
            //         }
            //     }
            //
            $data = $this->safe_value($response, 'data', array());
            $resultList = $this->safe_value($data, 'resultList', array());
            $result = array();
            for ($i = 0; $i < count($resultList); $i++) {
                $entry = $resultList[$i];
                $timestamp = $this->safe_integer($entry, 'settleTime');
                $result[] = array(
                    'info' => $entry,
                    'symbol' => $symbol,
                    'code' => null,
                    'timestamp' => $timestamp,
                    'datetime' => $this->iso8601($timestamp),
                    'id' => $this->safe_number($entry, 'id'),
                    'amount' => $this->safe_number($entry, 'funding'),
                );
            }
            return $result;
        }) ();
    }

    public function parse_funding_rate($contract, ?array $market = null): array {
        //
        //     {
        //         "symbol" => "BTC_USDT",
        //         "fundingRate" => 0.000014,
        //         "maxFundingRate" => 0.003,
        //         "minFundingRate" => -0.003,
        //         "collectCycle" => 8,
        //         "nextSettleTime" => 1643241600000,
        //         "timestamp" => 1643240373359
        //     }
        //
        $nextFundingRate = $this->safe_number($contract, 'fundingRate');
        $nextFundingTimestamp = $this->safe_integer($contract, 'nextSettleTime');
        $marketId = $this->safe_string($contract, 'symbol');
        $symbol = $this->safe_symbol($marketId, $market, null, 'contract');
        $timestamp = $this->safe_integer($contract, 'timestamp');
        $interval = $this->safe_string($contract, 'collectCycle');
        $intervalString = null;
        if ($interval !== null) {
            $intervalString = $interval . 'h';
        }
        return array(
            'info' => $contract,
            'symbol' => $symbol,
            'markPrice' => null,
            'indexPrice' => null,
            'interestRate' => null,
            'estimatedSettlePrice' => null,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'fundingRate' => $nextFundingRate,
            'fundingTimestamp' => $nextFundingTimestamp,
            'fundingDatetime' => $this->iso8601($nextFundingTimestamp),
            'nextFundingRate' => null,
            'nextFundingTimestamp' => null,
            'nextFundingDatetime' => null,
            'previousFundingRate' => null,
            'previousFundingTimestamp' => null,
            'previousFundingDatetime' => null,
            'interval' => $intervalString,
        );
    }

    public function fetch_funding_interval(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the current funding rate interval
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-contract-funding-rate
             *
             * @param {string} $symbol unified market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=funding-rate-structure funding rate structure~
             */
            return Async\await($this->fetch_funding_rate($symbol, $params));
        }) ();
    }

    public function fetch_funding_rate(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the current funding rate
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-contract-funding-rate
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=funding-rate-structure funding rate structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->contractPublicGetFundingRateSymbol ($this->extend($request, $params)));
            //
            //     {
            //         "success" => true,
            //         "code" => 0,
            //         "data" => {
            //             "symbol" => "BTC_USDT",
            //             "fundingRate" => 0.000014,
            //             "maxFundingRate" => 0.003,
            //             "minFundingRate" => -0.003,
            //             "collectCycle" => 8,
            //             "nextSettleTime" => 1643241600000,
            //             "timestamp" => 1643240373359
            //         }
            //     }
            //
            $result = $this->safe_value($response, 'data', array());
            return $this->parse_funding_rate($result, $market);
        }) ();
    }

    public function fetch_funding_rate_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches historical funding rate prices
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-contract-funding-rate-history
             *
             * @param {string} $symbol unified $symbol of the $market to fetch the funding rate history for
             * @param {int} [$since] not used by mexc, but filtered internally by ccxt
             * @param {int} [$limit] mexc $limit is page_size default 20, maximum is 100
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rate-history-structure funding rate structures~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchFundingRateHistory() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
                // 'page_size' => $limit, // optional
                // 'page_num' => 1, // optional, current page number, default is 1
            );
            if ($limit !== null) {
                $request['page_size'] = $limit;
            }
            $response = Async\await($this->contractPublicGetFundingRateHistory ($this->extend($request, $params)));
            //
            //    {
            //        "success" => true,
            //        "code" => 0,
            //        "data" => {
            //            "pageSize" => 2,
            //            "totalCount" => 21,
            //            "totalPage" => 11,
            //            "currentPage" => 1,
            //            "resultList" => array(
            //                array(
            //                    "symbol" => "BTC_USDT",
            //                    "fundingRate" => 0.000266,
            //                    "settleTime" => 1609804800000
            //                ),
            //                {
            //                    "symbol" => "BTC_USDT",
            //                    "fundingRate" => 0.00029,
            //                    "settleTime" => 1609776000000
            //                }
            //            )
            //        }
            //    }
            //
            $data = $this->safe_value($response, 'data');
            $result = $this->safe_value($data, 'resultList', array());
            $rates = array();
            for ($i = 0; $i < count($result); $i++) {
                $entry = $result[$i];
                $marketId = $this->safe_string($entry, 'symbol');
                $symbolInner = $this->safe_symbol($marketId);
                $timestamp = $this->safe_integer($entry, 'settleTime');
                $rates[] = array(
                    'info' => $entry,
                    'symbol' => $symbolInner,
                    'fundingRate' => $this->safe_number($entry, 'fundingRate'),
                    'timestamp' => $timestamp,
                    'datetime' => $this->iso8601($timestamp),
                );
            }
            $sorted = $this->sort_by($rates, 'timestamp');
            return $this->filter_by_symbol_since_limit($sorted, $market['symbol'], $since, $limit);
        }) ();
    }

    public function fetch_leverage_tiers(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes, if a market has a leverage tier of 0, then the leverage tiers cannot be obtained for this market
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-contract-information
             *
             * @param {string[]} [$symbols] list of unified market $symbols
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=leverage-tiers-structure leverage tiers structures~, indexed by market $symbols
             */
            Async\await($this->load_markets());
            $symbols = $this->market_symbols($symbols, 'swap', true, true);
            $response = Async\await($this->contractPublicGetDetail ($params));
            //
            //     {
            //         "success":true,
            //         "code":0,
            //         "data":[
            //             array(
            //                 "symbol" => "BTC_USDT",
            //                 "displayName" => "BTC_USDT永续",
            //                 "displayNameEn" => "BTC_USDT SWAP",
            //                 "positionOpenType" => 3,
            //                 "baseCoin" => "BTC",
            //                 "quoteCoin" => "USDT",
            //                 "settleCoin" => "USDT",
            //                 "contractSize" => 0.0001,
            //                 "minLeverage" => 1,
            //                 "maxLeverage" => 125,
            //                 "priceScale" => 2,
            //                 "volScale" => 0,
            //                 "amountScale" => 4,
            //                 "priceUnit" => 0.5,
            //                 "volUnit" => 1,
            //                 "minVol" => 1,
            //                 "maxVol" => 1000000,
            //                 "bidLimitPriceRate" => 0.1,
            //                 "askLimitPriceRate" => 0.1,
            //                 "takerFeeRate" => 0.0006,
            //                 "makerFeeRate" => 0.0002,
            //                 "maintenanceMarginRate" => 0.004,
            //                 "initialMarginRate" => 0.008,
            //                 "riskBaseVol" => 10000,
            //                 "riskIncrVol" => 200000,
            //                 "riskIncrMmr" => 0.004,
            //                 "riskIncrImr" => 0.004,
            //                 "riskLevelLimit" => 5,
            //                 "priceCoefficientVariation" => 0.1,
            //                 "indexOrigin" => ["BINANCE","GATEIO","HUOBI","MXC"],
            //                 "state" => 0, // 0 enabled, 1 delivery, 2 completed, 3 offline, 4 pause
            //                 "isNew" => false,
            //                 "isHot" => true,
            //                 "isHidden" => false
            //             ),
            //             ...
            //         ]
            //     }
            //
            $data = $this->safe_list($response, 'data');
            return $this->parse_leverage_tiers($data, $symbols, 'symbol');
        }) ();
    }

    public function parse_market_leverage_tiers($info, ?array $market = null): array {
        //
        //    {
        //        "symbol" => "BTC_USDT",
        //        "displayName" => "BTC_USDT永续",
        //        "displayNameEn" => "BTC_USDT SWAP",
        //        "positionOpenType" => 3,
        //        "baseCoin" => "BTC",
        //        "quoteCoin" => "USDT",
        //        "settleCoin" => "USDT",
        //        "contractSize" => 0.0001,
        //        "minLeverage" => 1,
        //        "maxLeverage" => 125,
        //        "priceScale" => 2,
        //        "volScale" => 0,
        //        "amountScale" => 4,
        //        "priceUnit" => 0.5,
        //        "volUnit" => 1,
        //        "minVol" => 1,
        //        "maxVol" => 1000000,
        //        "bidLimitPriceRate" => 0.1,
        //        "askLimitPriceRate" => 0.1,
        //        "takerFeeRate" => 0.0006,
        //        "makerFeeRate" => 0.0002,
        //        "maintenanceMarginRate" => 0.004,
        //        "initialMarginRate" => 0.008,
        //        "riskBaseVol" => 10000,
        //        "riskIncrVol" => 200000,
        //        "riskIncrMmr" => 0.004,
        //        "riskIncrImr" => 0.004,
        //        "riskLevelLimit" => 5,
        //        "priceCoefficientVariation" => 0.1,
        //        "indexOrigin" => ["BINANCE","GATEIO","HUOBI","MXC"],
        //        "state" => 0, // 0 enabled, 1 delivery, 2 completed, 3 offline, 4 pause
        //        "isNew" => false,
        //        "isHot" => true,
        //        "isHidden" => false
        //    }
        //
        $marketId = $this->safe_string($info, 'symbol');
        $maintenanceMarginRate = $this->safe_string($info, 'maintenanceMarginRate');
        $initialMarginRate = $this->safe_string($info, 'initialMarginRate');
        $maxVol = $this->safe_string($info, 'maxVol');
        $riskIncrVol = $this->safe_string($info, 'riskIncrVol');
        $riskIncrMmr = $this->safe_string($info, 'riskIncrMmr');
        $riskIncrImr = $this->safe_string($info, 'riskIncrImr');
        $floor = '0';
        $tiers = array();
        $quoteId = $this->safe_string($info, 'quoteCoin');
        if ($riskIncrVol === '0') {
            return array(
                array(
                    'tier' => 0,
                    'symbol' => $this->safe_symbol($marketId, $market, null, 'contract'),
                    'currency' => $this->safe_currency_code($quoteId),
                    'minNotional' => null,
                    'maxNotional' => null,
                    'maintenanceMarginRate' => null,
                    'maxLeverage' => $this->safe_number($info, 'maxLeverage'),
                    'info' => $info,
                ),
            );
        }
        while (Precise::string_lt($floor, $maxVol)) {
            $cap = Precise::string_add($floor, $riskIncrVol);
            $tiers[] = array(
                'tier' => $this->parse_number(Precise::string_div($cap, $riskIncrVol)),
                'symbol' => $this->safe_symbol($marketId, $market, null, 'contract'),
                'currency' => $this->safe_currency_code($quoteId),
                'minNotional' => $this->parse_number($floor),
                'maxNotional' => $this->parse_number($cap),
                'maintenanceMarginRate' => $this->parse_number($maintenanceMarginRate),
                'maxLeverage' => $this->parse_number(Precise::string_div('1', $initialMarginRate)),
                'info' => $info,
            );
            $initialMarginRate = Precise::string_add($initialMarginRate, $riskIncrImr);
            $maintenanceMarginRate = Precise::string_add($maintenanceMarginRate, $riskIncrMmr);
            $floor = $cap;
        }
        return $tiers;
    }

    public function parse_deposit_address($depositAddress, ?array $currency = null): array {
        //
        //    {
        //        coin => "USDT",
        //        network => "BNB Smart Chain(BEP20)",
        //        $address => "0x0d48003e0c27c5de62b97c9b4cdb31fdd29da619",
        //        memo =>  null
        //    }
        //
        $address = $this->safe_string($depositAddress, 'address');
        $currencyId = $this->safe_string($depositAddress, 'coin');
        $networkId = $this->safe_string($depositAddress, 'netWork');
        $this->check_address($address);
        return array(
            'info' => $depositAddress,
            'currency' => $this->safe_currency_code($currencyId, $currency),
            'network' => $this->network_id_to_code($networkId, $currencyId),
            'address' => $address,
            'tag' => $this->safe_string($depositAddress, 'memo'),
        );
    }

    public function fetch_deposit_addresses_by_network(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch a dictionary of addresses for a $currency, indexed by $network
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#deposit-address-supporting-$network
             *
             * @param {string} $code unified $currency $code of the $currency for the deposit address
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=address-structure address structures~ indexed by the $network
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
            );
            $networkCode = $this->safe_string($params, 'network');
            $networkId = null;
            if ($networkCode !== null) {
                // createDepositAddress and fetchDepositAddress use a different $network-id compared to withdraw
                $networkUnified = $this->network_id_to_code($networkCode, $code);
                $networks = $this->safe_dict($currency, 'networks', array());
                if (is_array($networks) && array_key_exists($networkUnified, $networks)) {
                    $network = $this->safe_dict($networks, $networkUnified, array());
                    $networkInfo = $this->safe_value($network, 'info', array());
                    $networkId = $this->safe_string($networkInfo, 'network');
                } else {
                    $networkId = $this->network_code_to_id($networkCode, $code);
                }
            }
            if ($networkId !== null) {
                $request['network'] = $networkId;
            }
            $params = $this->omit($params, 'network');
            $response = Async\await($this->spotPrivateGetCapitalDepositAddress ($this->extend($request, $params)));
            //
            //    array(
            //        {
            //            coin => "USDT",
            //            $network => "BNB Smart Chain(BEP20)",
            //            address => "0x0d48003e0c27c5de62b97c9b4cdb31fdd29da619",
            //            memo =>  null
            //        }
            //        ...
            //    )
            //
            $addressStructures = $this->parse_deposit_addresses($response, null, false);
            return $this->index_by($addressStructures, 'network');
        }) ();
    }

    public function create_deposit_address(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * create a $currency deposit address
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#generate-deposit-address-supporting-$network
             *
             * @param {string} $code unified $currency $code of the $currency for the deposit address
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->network] the blockchain $network name
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $request = array(
                'coin' => $currency['id'],
            );
            $networkCode = $this->safe_string($params, 'network');
            if ($networkCode === null) {
                throw new ArgumentsRequired($this->id . ' createDepositAddress requires a `$network` parameter');
            }
            // createDepositAddress and fetchDepositAddress use a different $network-id compared to withdraw
            $networkId = null;
            $networkUnified = $this->network_id_to_code($networkCode, $code);
            $networks = $this->safe_dict($currency, 'networks', array());
            if (is_array($networks) && array_key_exists($networkUnified, $networks)) {
                $network = $this->safe_dict($networks, $networkUnified, array());
                $networkInfo = $this->safe_value($network, 'info', array());
                $networkId = $this->safe_string($networkInfo, 'network');
            } else {
                $networkId = $this->network_code_to_id($networkCode, $code);
            }
            if ($networkId !== null) {
                $request['network'] = $networkId;
            }
            $params = $this->omit($params, 'network');
            $response = Async\await($this->spotPrivatePostCapitalDepositAddress ($this->extend($request, $params)));
            //     {
            //        "coin" => "EOS",
            //        "network" => "EOS",
            //        "address" => "zzqqqqqqqqqq",
            //        "memo" => "MX10068"
            //     }
            return $this->parse_deposit_address($response, $currency);
        }) ();
    }

    public function fetch_deposit_address(string $code, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $params) {
            /**
             * fetch the deposit address for a currency associated with this account
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#deposit-address-supporting-$network
             *
             * @param {string} $code unified currency $code
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->network] the chain of currency, this only apply for multi-chain currency, and there is no need for single chain currency
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=address-structure address structure~
             */
            $network = $this->safe_string($params, 'network');
            $addressStructures = Async\await($this->fetch_deposit_addresses_by_network($code, $params));
            $result = null;
            if ($network !== null) {
                $result = $this->safe_dict($addressStructures, $this->network_id_to_code($network, $code));
            } else {
                $options = $this->safe_dict($this->options, 'defaultNetworks');
                $defaultNetworkForCurrency = $this->safe_string($options, $code);
                if ($defaultNetworkForCurrency !== null) {
                    $result = $this->safe_dict($addressStructures, $defaultNetworkForCurrency);
                } else {
                    $keys = is_array($addressStructures) ? array_keys($addressStructures) : array();
                    $key = $this->safe_string($keys, 0);
                    $result = $this->safe_dict($addressStructures, $key);
                }
            }
            if ($result === null) {
                throw new InvalidAddress($this->id . ' fetchDepositAddress() cannot find a deposit address for ' . $code . ', and network' . $network . 'consider creating one using .createDepositAddress() method or in MEXC website');
            }
            return $result;
        }) ();
    }

    public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all deposits made to an account
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#deposit-history-supporting-network
             *
             * @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~
             */
            Async\await($this->load_markets());
            $request = array(
                // 'coin' => $currency['id'] . network example => USDT-TRX,
                // 'status' => 'status',
                // 'startTime' => $since, // default 90 days
                // 'endTime' => $this->nonce(),
                // 'limit' => $limit, // default 1000, maximum 1000
            );
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
                $request['coin'] = $currency['id'];
                // currently mexc does not have network names unified so for certain things we might need TRX or TRC-20
                // due to that I'm applying the network parameter directly so the user can control it on its side
                $rawNetwork = $this->safe_string($params, 'network');
                if ($rawNetwork !== null) {
                    $params = $this->omit($params, 'network');
                    $request['coin'] = $request['coin'] . '-' . $rawNetwork;
                }
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                if ($limit > 1000) {
                    throw new ExchangeError('This exchange supports a maximum $limit of 1000');
                }
                $request['limit'] = $limit;
            }
            $response = Async\await($this->spotPrivateGetCapitalDepositHisrec ($this->extend($request, $params)));
            //
            // array(
            //     {
            //         "amount" => "10",
            //         "coin" => "USDC-TRX",
            //         "network" => "TRX",
            //         "status" => "5",
            //         "address" => "TSMcEDDvkqY9dz8RkFnrS86U59GwEZjfvh",
            //         "txId" => "51a8f49e6f03f2c056e71fe3291aa65e1032880be855b65cecd0595a1b8af95b:0",
            //         "insertTime" => "1664805021000",
            //         "unlockConfirm" => "200",
            //         "confirmTimes" => "203",
            //         "memo" => "xxyy1122",
            //         "transHash" => "51a8f49e6f03f2c056e71fe3291aa65e1032880be855b65cecd0595a1b8af95b",
            //         "updateTime" => "1664805621000",
            //         "netWork => "TRX"
            //     }
            // )
            //
            return $this->parse_transactions($response, $currency, $since, $limit);
        }) ();
    }

    public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all withdrawals made from an account
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#withdraw-history-supporting-network
             *
             * @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~
             */
            Async\await($this->load_markets());
            $request = array(
                // 'coin' => $currency['id'],
                // 'status' => 'status',
                // 'startTime' => $since, // default 90 days
                // 'endTime' => $this->nonce(),
                // 'limit' => $limit, // default 1000, maximum 1000
            );
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
                $request['coin'] = $currency['id'];
            }
            if ($since !== null) {
                $request['startTime'] = $since;
            }
            if ($limit !== null) {
                if ($limit > 1000) {
                    throw new ExchangeError('This exchange supports a maximum $limit of 1000');
                }
                $request['limit'] = $limit;
            }
            $response = Async\await($this->spotPrivateGetCapitalWithdrawHistory ($this->extend($request, $params)));
            //
            // array(
            //     {
            //       "id" => "adcd1c8322154de691b815eedcd10c42",
            //       "txId" => "0xc8c918cd69b2246db493ef6225a72ffdc664f15b08da3e25c6879b271d05e9d0:0",
            //       "coin" => "USDC-MATIC",
            //       "network" => "MATIC",
            //       "address" => "0xeE6C7a415995312ED52c53a0f8f03e165e0A5D62",
            //       "amount" => "2",
            //       "transferType" => "0",
            //       "status" => "7",
            //       "transactionFee" => "1",
            //       "confirmNo" => null,
            //       "applyTime" => "1664882739000",
            //       "remark" => '',
            //       "memo" => null,
            //       "explorerUrl" => "https://etherscan.io/tx/0xc8c918cd69b2246db493ef6225a72ffdc664f15b08da3e25c6879b271d05e9d0",
            //       "transHash" => "0xc8c918cd69b2246db493ef6225a72ffdc664f15b08da3e25c6879b271d05e9d0",
            //       "updateTime" => "1664882799000",
            //       "netWork => "MATIC"
            //     }
            // )
            //
            return $this->parse_transactions($response, $currency, $since, $limit);
        }) ();
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // fetchDeposits
        //
        // {
        //     "amount" => "10",
        //     "coin" => "USDC-TRX",
        //     "network" => "TRX",
        //     "status" => "5",
        //     "address" => "TSMcEDDvkqY9dz8RkFnrS86U59GwEZjfvh",
        //     "txId" => "51a8f49e6f03f2c056e71fe3291aa65e1032880be855b65cecd0595a1b8af95b:0",
        //     "insertTime" => "1664805021000",
        //     "unlockConfirm" => "200",
        //     "confirmTimes" => "203",
        //     "memo" => "xxyy1122",
        //     "transHash" => "51a8f49e6f03f2c056e71fe3291aa65e1032880be855b65cecd0595a1b8af95b",
        //     "updateTime" => "1664805621000",
        //     "netWork => "TRX"
        // }
        //
        // fetchWithdrawals
        //
        // {
        //     "id" => "adcd1c8322154de691b815eedcd10c42",
        //     "txId" => "0xc8c918cd69b2246db493ef6225a72ffdc664f15b08da3e25c6879b271d05e9d0:0",
        //     "coin" => "USDC-MATIC",
        //     "network" => "MATIC",
        //     "address" => "0xeE6C7a415995312ED52c53a0f8f03e165e0A5D62",
        //     "amount" => "2",
        //     "transferType" => "0",
        //     "status" => "7",
        //     "transactionFee" => "1",
        //     "confirmNo" => null,
        //     "applyTime" => "1664882739000",
        //     "remark" => "",
        //     "memo" => null,
        //     "explorerUrl" => "https://etherscan.io/tx/0xc8c918cd69b2246db493ef6225a72ffdc664f15b08da3e25c6879b271d05e9d0",
        //     "transHash" => "0xc8c918cd69b2246db493ef6225a72ffdc664f15b08da3e25c6879b271d05e9d0",
        //     "updateTime" => "1664882799000",
        //     "netWork => "MATIC"
        //   }
        //
        // withdraw
        //
        //     {
        //         "id":"25fb2831fb6d4fc7aa4094612a26c81d"
        //     }
        //
        // internal withdraw (aka internal-transfer)
        //
        //     {
        //         "tranId":"ad36f0e9c9a24ae794b36fa4f152e471"
        //     }
        //
        $id = $this->safe_string_2($transaction, 'id', 'tranId');
        $type = ($id === null) ? 'deposit' : 'withdrawal';
        $timestamp = $this->safe_integer_2($transaction, 'insertTime', 'applyTime');
        $updated = $this->safe_integer($transaction, 'updateTime');
        $currencyId = null;
        $currencyWithNetwork = $this->safe_string($transaction, 'coin');
        if ($currencyWithNetwork !== null) {
            $currencyId = explode('-', $currencyWithNetwork)[0];
        }
        $network = null;
        $rawNetwork = $this->safe_string($transaction, 'network');
        if ($rawNetwork !== null) {
            $network = $this->network_id_to_code($rawNetwork);
        }
        $code = $this->safe_currency_code($currencyId, $currency);
        $status = $this->parse_transaction_status_by_type($this->safe_string($transaction, 'status'), $type);
        $amountString = $this->safe_string($transaction, 'amount');
        $address = $this->safe_string($transaction, 'address');
        $txid = $this->safe_string_2($transaction, 'transHash', 'txId');
        $fee = null;
        $feeCostString = $this->safe_string($transaction, 'transactionFee');
        if ($feeCostString !== null) {
            $fee = array(
                'cost' => $this->parse_number($feeCostString),
                'currency' => $code,
            );
        }
        if ($type === 'withdrawal') {
            // mexc withdrawal amount includes the $fee
            $amountString = Precise::string_sub($amountString, $feeCostString);
        }
        return array(
            'info' => $transaction,
            'id' => $id,
            'txid' => $txid,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'network' => $network,
            'address' => $address,
            'addressTo' => $address,
            'addressFrom' => null,
            'tag' => $this->safe_string($transaction, 'memo'),
            'tagTo' => null,
            'tagFrom' => null,
            'type' => $type,
            'amount' => $this->parse_number($amountString),
            'currency' => $code,
            'status' => $status,
            'updated' => $updated,
            'comment' => $this->safe_string($transaction, 'remark'),
            'internal' => null,
            'fee' => $fee,
        );
    }

    public function parse_transaction_status_by_type($status, $type = null) {
        $statusesByType = array(
            'deposit' => array(
                '1' => 'failed', // SMALL
                '2' => 'pending', // TIME_DELAY
                '3' => 'pending', // LARGE_DELAY
                '4' => 'pending', // PENDING
                '5' => 'ok', // SUCCESS
                '6' => 'pending', // AUDITING
                '7' => 'failed', // REJECTED
            ),
            'withdrawal' => array(
                '1' => 'pending', // APPLY
                '2' => 'pending', // AUDITING
                '3' => 'pending', // WAIT
                '4' => 'pending', // PROCESSING
                '5' => 'pending', // WAIT_PACKAGING
                '6' => 'pending', // WAIT_CONFIRM
                '7' => 'ok', // SUCCESS
                '8' => 'failed', // FAILED
                '9' => 'canceled', // CANCEL
                '10' => 'pending', // MANUAL
            ),
        );
        $statuses = $this->safe_value($statusesByType, $type, array());
        return $this->safe_string($statuses, $status, $status);
    }

    public function fetch_position(string $symbol, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch data on a single open contract trade position
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-user-s-history-position-information
             *
             * @param {string} $symbol unified $market $symbol of the $market the position is held in, default is null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->fetch_positions(null, $this->extend($request, $params)));
            return $this->safe_value($response, 0);
        }) ();
    }

    public function fetch_positions(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetch all open positions
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-user-s-history-position-information
             *
             * @param {string[]|null} $symbols list of unified market $symbols
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $response = Async\await($this->contractPrivateGetPositionOpenPositions ($params));
            //
            //     {
            //         "success" => true,
            //         "code" => 0,
            //         "data" => array(
            //             {
            //                 "positionId" => 1394650,
            //                 "symbol" => "ETH_USDT",
            //                 "positionType" => 1,
            //                 "openType" => 1,
            //                 "state" => 1,
            //                 "holdVol" => 1,
            //                 "frozenVol" => 0,
            //                 "closeVol" => 0,
            //                 "holdAvgPrice" => 1217.3,
            //                 "openAvgPrice" => 1217.3,
            //                 "closeAvgPrice" => 0,
            //                 "liquidatePrice" => 1211.2,
            //                 "oim" => 0.1290338,
            //                 "im" => 0.1290338,
            //                 "holdFee" => 0,
            //                 "realised" => -0.0073,
            //                 "leverage" => 100,
            //                 "createTime" => 1609991676000,
            //                 "updateTime" => 1609991676000,
            //                 "autoAddIm" => false
            //             }
            //         )
            //     }
            //
            $data = $this->safe_list($response, 'data', array());
            return $this->parse_positions($data, $symbols);
        }) ();
    }

    public function parse_position(array $position, ?array $market = null) {
        //
        // fetchPositions
        //
        //     {
        //         "positionId" => 1394650,
        //         "symbol" => "ETH_USDT",
        //         "positionType" => 1,
        //         "openType" => 1,
        //         "state" => 1,
        //         "holdVol" => 1,
        //         "frozenVol" => 0,
        //         "closeVol" => 0,
        //         "holdAvgPrice" => 1217.3,
        //         "openAvgPrice" => 1217.3,
        //         "closeAvgPrice" => 0,
        //         "liquidatePrice" => 1211.2,
        //         "oim" => 0.1290338,
        //         "im" => 0.1290338,
        //         "holdFee" => 0,
        //         "realised" => -0.0073,
        //         "leverage" => 100,
        //         "createTime" => 1609991676000,
        //         "updateTime" => 1609991676000,
        //         "autoAddIm" => false
        //     }
        //
        // fetchPositionsHistory
        //
        //    {
        //        positionId => '390281084',
        //        $symbol => 'RVN_USDT',
        //        positionType => '1',
        //        $openType => '2',
        //        state => '3',
        //        holdVol => '0',
        //        frozenVol => '0',
        //        closeVol => '1141',
        //        holdAvgPrice => '0.03491',
        //        holdAvgPriceFullyScale => '0.03491',
        //        openAvgPrice => '0.03491',
        //        openAvgPriceFullyScale => '0.03491',
        //        closeAvgPrice => '0.03494',
        //        liquidatePrice => '0.03433',
        //        oim => '0',
        //        im => '0',
        //        holdFee => '0',
        //        realised => '0.1829',
        //        $leverage => '50',
        //        createTime => '1711512408000',
        //        updateTime => '1711512553000',
        //        autoAddIm => false,
        //        version => '4',
        //        profitRatio => '0.0227',
        //        newOpenAvgPrice => '0.03491',
        //        newCloseAvgPrice => '0.03494',
        //        closeProfitLoss => '0.3423',
        //        fee => '0.1593977',
        //        positionShowStatus => 'CLOSED'
        //    }
        //
        $market = $this->safe_market($this->safe_string($position, 'symbol'), $market, null, 'swap');
        $symbol = $market['symbol'];
        $contracts = $this->safe_string($position, 'holdVol');
        $entryPrice = $this->safe_number($position, 'openAvgPrice');
        $initialMargin = $this->safe_string($position, 'im');
        $rawSide = $this->safe_string($position, 'positionType');
        $side = ($rawSide === '1') ? 'long' : 'short';
        $openType = $this->safe_string($position, 'margin_mode');
        $marginType = ($openType === '1') ? 'isolated' : 'cross';
        $leverage = $this->safe_number($position, 'leverage');
        $liquidationPrice = $this->safe_number($position, 'liquidatePrice');
        $timestamp = $this->safe_integer($position, 'updateTime');
        return $this->safe_position(array(
            'info' => $position,
            'id' => null,
            'symbol' => $symbol,
            'contracts' => $this->parse_number($contracts),
            'contractSize' => null,
            'entryPrice' => $entryPrice,
            'collateral' => null,
            'side' => $side,
            'unrealizedPnl' => null,
            'leverage' => $this->parse_number($leverage),
            'percentage' => null,
            'marginMode' => $marginType,
            'notional' => null,
            'markPrice' => null,
            'lastPrice' => null,
            'liquidationPrice' => $liquidationPrice,
            'initialMargin' => $this->parse_number($initialMargin),
            'initialMarginPercentage' => null,
            'maintenanceMargin' => null,
            'maintenanceMarginPercentage' => null,
            'marginRatio' => null,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'hedged' => null,
            'stopLossPrice' => null,
            'takeProfitPrice' => null,
            'lastUpdateTimestamp' => null,
        ));
    }

    public function fetch_transfer(string $id, ?string $code = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($id, $code, $params) {
            /**
             * fetches a transfer
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v2_en/#internal-assets-transfer-order-inquiry
             *
             * @param {string} $id transfer $id
             * @param {string} [$code] not used by mexc fetchTransfer
             * @param {array} $params extra parameters specific to the exchange api endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?$id=transfer-structure transfer structure~
             */
            list($marketType, $query) = $this->handle_market_type_and_params('fetchTransfer', null, $params);
            Async\await($this->load_markets());
            if ($marketType === 'spot') {
                $request = array(
                    'transact_id' => $id,
                );
                $response = Async\await($this->spotPrivateGetAssetInternalTransferRecord ($this->extend($request, $query)));
                //
                //     {
                //         "code" => "200",
                //         "data" => {
                //             "currency" => "USDT",
                //             "amount" => "1",
                //             "transact_id" => "954877a2ef54499db9b28a7cf9ebcf41",
                //             "from" => "MAIN",
                //             "to" => "CONTRACT",
                //             "transact_state" => "SUCCESS"
                //         }
                //     }
                //
                $data = $this->safe_dict($response, 'data', array());
                return $this->parse_transfer($data);
            } elseif ($marketType === 'swap') {
                throw new BadRequest($this->id . ' fetchTransfer() is not supported for ' . $marketType);
            }
            return null;
        }) ();
    }

    public function fetch_transfers(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch a history of internal transfers made on an account
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v2_en/#get-internal-assets-transfer-records
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-user-39-s-asset-transfer-records
             * @see https://www.mexc.com/api-docs/spot-v3/wallet-endpoints#query-user-universal-transfer-history     * @param {string} $code unified $currency $code of the $currency transferred
             *
             * @param $code
             * @param {int} [$since] the earliest time in ms to fetch transfers for
             * @param {int} [$limit] the maximum number of  transfers structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->fromAccountType] 'SPOT' for spot wallet, 'FUTURES' for contract wallet
             * @param {string} [$params->toAccountType] 'SPOT' for spot wallet, 'FUTURES' for contract wallet
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transfer-structure transfer structures~
             */
            $marketType = null;
            list($marketType, $params) = $this->handle_market_type_and_params('fetchTransfers', null, $params);
            Async\await($this->load_markets());
            $request = array();
            $currency = null;
            if ($code !== null) {
                $currency = $this->currency($code);
            }
            $fromAccountType = null;
            list($fromAccountType, $params) = $this->handle_option_and_params($params, 'fetchTransfers', 'fromAccountType');
            $accountTypes = array(
                'spot' => 'SPOT',
                'swap' => 'FUTURES',
                'futures' => 'FUTURES',
                'future' => 'FUTURES',
                'margin' => 'SPOT',
            );
            if ($fromAccountType !== null) {
                $request['fromAccountType'] = $this->safe_string($accountTypes, $fromAccountType, $fromAccountType);
            } else {
                throw new ArgumentsRequired($this->id . ' fetchTransfers() requires a $fromAccountType parameter, one of "SPOT", "FUTURES"');
            }
            $toAccountType = null;
            list($toAccountType, $params) = $this->handle_option_and_params($params, 'fetchTransfers', 'toAccountType');
            if ($toAccountType !== null) {
                $request['toAccountType'] = $this->safe_string($accountTypes, $toAccountType, $toAccountType);
            } else {
                throw new ArgumentsRequired($this->id . ' fetchTransfers() requires a $toAccountType parameter, one of "SPOT", "FUTURES"');
            }
            $resultList = array();
            if ($marketType === 'spot') {
                if ($since !== null) {
                    $request['startTime'] = $since;
                }
                if ($limit !== null) {
                    if ($limit > 100) {
                        throw new ExchangeError('This exchange supports a maximum $limit of 50');
                    }
                    $request['size'] = $limit;
                }
                $response = Async\await($this->spotPrivateGetCapitalTransfer ($this->extend($request, $params)));
                //
                //
                // {
                //     "rows" => array(
                //         {
                //         "tranId" => "cdf0d2a618b5458c965baefe6b1d0859",
                //         "clientTranId" => null,
                //         "asset" => "USDT",
                //         "amount" => "1",
                //         "fromAccountType" => "FUTURES",
                //         "toAccountType" => "SPOT",
                //         "symbol" => null,
                //         "status" => "SUCCESS",
                //         "timestamp" => 1759328309000
                //         }
                //     ),
                //     "total" => 1
                // }
                $resultList = $this->safe_list($response, 'rows', array());
            } elseif ($marketType === 'swap') {
                if ($limit !== null) {
                    $request['page_size'] = $limit;
                }
                $response = Async\await($this->contractPrivateGetAccountTransferRecord ($this->extend($request, $params)));
                $data = $this->safe_value($response, 'data');
                $resultList = $this->safe_value($data, 'resultList');
                //
                //     {
                //         "success" => true,
                //         "code" => "0",
                //         "data" => {
                //             "pageSize" => "20",
                //             "totalCount" => "10",
                //             "totalPage" => "1",
                //             "currentPage" => "1",
                //             "resultList" => array(
                //                 array(
                //                     "id" => "2980812",
                //                     "txid" => "fa8a1e7bf05940a3b7025856dc48d025",
                //                     "currency" => "USDT",
                //                     "amount" => "22.90213135",
                //                     "type" => "IN",
                //                     "state" => "SUCCESS",
                //                     "createTime" => "1648849076000",
                //                     "updateTime" => "1648849076000"
                //                 ),
                //             )
                //         }
                //     }
                //
            }
            return $this->parse_transfers($resultList, $currency, $since, $limit);
        }) ();
    }

    public function transfer(string $code, float $amount, string $fromAccount, string $toAccount, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $fromAccount, $toAccount, $params) {
            /**
             * transfer $currency internally between wallets on the same account
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#user-universal-transfer
             *
             * @param {string} $code unified $currency $code
             * @param {float} $amount amount to transfer
             * @param {string} $fromAccount account to transfer from
             * @param {string} $toAccount account to transfer to
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->symbol] $market $symbol required for margin account transfers eg:BTCUSDT
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=transfer-structure transfer structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            $accounts = array(
                'spot' => 'SPOT',
                'swap' => 'FUTURES',
                'future' => 'FUTURES',
            );
            $fromId = $this->safe_string($accounts, $fromAccount, $fromAccount);
            $toId = $this->safe_string($accounts, $toAccount, $toAccount);
            if ($fromId === null) {
                $keys = is_array($accounts) ? array_keys($accounts) : array();
                throw new ExchangeError($this->id . ' $fromAccount must be one of ' . implode(', ', $keys));
            }
            if ($toId === null) {
                $keys = is_array($accounts) ? array_keys($accounts) : array();
                throw new ExchangeError($this->id . ' $toAccount must be one of ' . implode(', ', $keys));
            }
            $request = array(
                'asset' => $currency['id'],
                'amount' => $amount,
                'fromAccountType' => $fromId,
                'toAccountType' => $toId,
            );
            if (($fromId === 'ISOLATED_MARGIN') || ($toId === 'ISOLATED_MARGIN')) {
                $symbol = $this->safe_string($params, 'symbol');
                $params = $this->omit($params, 'symbol');
                if ($symbol === null) {
                    throw new ArgumentsRequired($this->id . ' transfer() requires a $symbol argument for isolated margin');
                }
                $market = $this->market($symbol);
                $request['symbol'] = $market['id'];
            }
            $response = Async\await($this->spotPrivatePostCapitalTransfer ($this->extend($request, $params)));
            //
            //     {
            //         "tranId" => "ebb06123e6a64f4ab234b396c548d57e"
            //     }
            //
            $transaction = $this->parse_transfer($response, $currency);
            return $this->extend($transaction, array(
                'amount' => $amount,
                'fromAccount' => $fromAccount,
                'toAccount' => $toAccount,
            ));
        }) ();
    }

    public function parse_transfer(array $transfer, ?array $currency = null): array {
        //
        // spot => fetchTransfer
        //
        //     {
        //         "currency" => "USDT",
        //         "amount" => "1",
        //         "transact_id" => "b60c1df8e7b24b268858003f374ecb75",
        //         "from" => "MAIN",
        //         "to" => "CONTRACT",
        //         "transact_state" => "WAIT"
        //     }
        //
        // swap => fetchTransfer
        //
        //     {
        //         "currency" => "USDT",
        //         "amount" => "22.90213135",
        //         "txid" => "fa8a1e7bf05940a3b7025856dc48d025",
        //         "id" => "2980812",
        //         "type" => "IN",
        //         "state" => "SUCCESS",
        //         "createTime" => "1648849076000",
        //         "updateTime" => "1648849076000"
        //     }
        //         {
        //         "tranId" => "cdf0d2a618b5458c965baefe6b1d0859",
        //         "clientTranId" => null,
        //         "asset" => "USDT",
        //         "amount" => "1",
        //         "fromAccountType" => "FUTURES",
        //         "toAccountType" => "SPOT",
        //         "symbol" => null,
        //         "status" => "SUCCESS",
        //         "timestamp" => 1759328309000
        //         }
        //
        // $transfer
        //
        //     {
        //         "tranId" => "ebb06123e6a64f4ab234b396c548d57e"
        //     }
        //
        $currencyId = $this->safe_string_2($transfer, 'currency', 'asset');
        $id = $this->safe_string_n($transfer, array( 'transact_id', 'txid', 'tranId' ));
        $timestamp = $this->safe_integer_2($transfer, 'createTime', 'timestamp');
        $datetime = ($timestamp !== null) ? $this->iso8601($timestamp) : null;
        $direction = $this->safe_string($transfer, 'type');
        $accountFrom = null;
        $accountTo = null;
        $fromAccountType = $this->safe_string($transfer, 'fromAccountType');
        $toAccountType = $this->safe_string($transfer, 'toAccountType');
        if (($fromAccountType !== null) && ($toAccountType !== null)) {
            $accountFrom = $fromAccountType;
            $accountTo = $toAccountType;
        } elseif ($direction !== null) {
            $accountFrom = ($direction === 'IN') ? 'MAIN' : 'CONTRACT';
            $accountTo = ($direction === 'IN') ? 'CONTRACT' : 'MAIN';
        } else {
            $accountFrom = $this->safe_string($transfer, 'from');
            $accountTo = $this->safe_string($transfer, 'to');
        }
        return array(
            'info' => $transfer,
            'id' => $id,
            'timestamp' => $timestamp,
            'datetime' => $datetime,
            'currency' => $this->safe_currency_code($currencyId, $currency),
            'amount' => $this->safe_number($transfer, 'amount'),
            'fromAccount' => $this->parse_account_id($accountFrom),
            'toAccount' => $this->parse_account_id($accountTo),
            'status' => $this->parse_transfer_status($this->safe_string_n($transfer, array( 'transact_state', 'state', 'status' ))),
        );
    }

    public function parse_account_id($status) {
        $statuses = array(
            'SPOT' => 'spot',
            'FUTURES' => 'swap',
            'MAIN' => 'spot',
            'CONTRACT' => 'swap',
        );
        return $this->safe_string($statuses, $status, $status);
    }

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

    public function withdraw(string $code, float $amount, string $address, ?string $tag = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $amount, $address, $tag, $params) {
            /**
             * make a withdrawal
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#withdraw-new
             * @see https://www.mexc.com/api-docs/spot-v3/wallet-endpoints#$internal-transfer
             *
             * @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
             * @param {array} [$params->internal] false by default, set to true for an "internal transfer"
             * @param {array} [$params->toAccountType] skipped by default, set to 'EMAIL|UID|MOBILE' when making an "internal transfer"
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~
             */
            Async\await($this->load_markets());
            $currency = $this->currency($code);
            list($tag, $params) = $this->handle_withdraw_tag_and_params($tag, $params);
            $internal = $this->safe_bool($params, 'internal', false);
            if ($internal) {
                $params = $this->omit($params, 'internal');
                $requestForInternal = array(
                    'asset' => $currency['id'],
                    'amount' => $amount,
                    'toAccount' => $address,
                );
                $toAccountType = $this->safe_string($params, 'toAccountType');
                if ($toAccountType === null) {
                    throw new ArgumentsRequired($this->id . ' withdraw() requires a $toAccountType parameter for $internal transfer to be of => EMAIL | UID | MOBILE');
                }
                $responseForInternal = Async\await($this->spotPrivatePostCapitalTransferInternal ($this->extend($requestForInternal, $params)));
                //
                //     {
                //       "id":"7213fea8e94b4a5593d507237e5a555b"
                //     }
                //
                return $this->parse_transaction($responseForInternal, $currency);
            }
            $networks = $this->safe_dict($this->options, 'networks', array());
            $network = $this->safe_string_2($params, 'network', 'netWork'); // this line allows the user to specify either ERC20 or ETH
            $network = $this->safe_string($networks, $network, $network); // handle ETH > ERC-20 alias
            $network = $this->network_code_to_id($network, $currency['code']);
            $this->check_address($address);
            $request = array(
                'coin' => $currency['id'],
                'address' => $address,
                'amount' => $amount,
            );
            if ($tag !== null) {
                $request['memo'] = $tag;
            }
            if ($network !== null) {
                $request['netWork'] = $network;
                $params = $this->omit($params, array( 'network', 'netWork' ));
            }
            $response = Async\await($this->spotPrivatePostCapitalWithdraw ($this->extend($request, $params)));
            //
            //     {
            //       "id":"7213fea8e94b4a5593d507237e5a555b"
            //     }
            //
            return $this->parse_transaction($response, $currency);
        }) ();
    }

    public function set_position_mode(bool $hedged, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($hedged, $symbol, $params) {
            /**
             * set $hedged to true or false for a market
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#change-position-mode
             *
             * @param {bool} $hedged set to true to use dualSidePosition
             * @param {string} $symbol not used by mexc setPositionMode ()
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} $response from the exchange
             */
            $request = array(
                'positionMode' => $hedged ? 1 : 2, // 1 Hedge, 2 One-way, before changing position mode make sure that there are no active orders, planned orders, or open positions, the risk limit level will be reset to 1
            );
            $response = Async\await($this->contractPrivatePostPositionChangePositionMode ($this->extend($request, $params)));
            //
            //     {
            //         "success":true,
            //         "code":0
            //     }
            //
            return $response;
        }) ();
    }

    public function fetch_position_mode(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetchs the position mode, hedged or one way, hedged for binance is set identically for all linear markets or all inverse markets
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-position-mode
             *
             * @param {string} $symbol not used by mexc fetchPositionMode
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an object detailing whether the market is in hedged or one-way mode
             */
            $response = Async\await($this->contractPrivateGetPositionPositionMode ($params));
            //
            //     {
            //         "success":true,
            //         "code":0,
            //         "data":2
            //     }
            //
            $positionMode = $this->safe_integer($response, 'data');
            return array(
                'info' => $response,
                'hedged' => ($positionMode === 1),
            );
        }) ();
    }

    public function fetch_transaction_fees(?array $codes = null, $params = array ()) {
        return Async\async(function () use ($codes, $params) {
            /**
             * fetch deposit and withdrawal fees
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#query-the-currency-information
             *
             * @param {string[]|null} $codes returns fees for all currencies if null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=fee-structure fee structures~
             */
            Async\await($this->load_markets());
            $response = Async\await($this->spotPrivateGetCapitalConfigGetall ($params));
            //
            //    array(
            //       {
            //           "coin" => "AGLD",
            //           "name" => "Adventure Gold",
            //           "networkList" => array(
            //               array(
            //                   "coin" => "AGLD",
            //                   "depositDesc" => null,
            //                   "depositEnable" => true,
            //                   "minConfirm" => "0",
            //                   "name" => "Adventure Gold",
            //                   "network" => "ERC20",
            //                   "withdrawEnable" => true,
            //                   "withdrawFee" => "10.000000000000000000",
            //                   "withdrawIntegerMultiple" => null,
            //                   "withdrawMax" => "1200000.000000000000000000",
            //                   "withdrawMin" => "20.000000000000000000",
            //                   "sameAddress" => false,
            //                   "contract" => "0x32353a6c91143bfd6c7d363b546e62a9a2489a20",
            //                   "withdrawTips" => null,
            //                   "depositTips" => null
            //               }
            //               ...
            //           )
            //       ),
            //       ...
            //    )
            //
            return $this->parse_transaction_fees($response, $codes);
        }) ();
    }

    public function parse_transaction_fees($response, $codes = null) {
        $withdrawFees = array();
        for ($i = 0; $i < count($response); $i++) {
            $entry = $response[$i];
            $currencyId = $this->safe_string($entry, 'coin');
            $currency = $this->safe_currency($currencyId);
            $code = $this->safe_string($currency, 'code');
            if (($codes === null) || ($this->in_array($code, $codes))) {
                $withdrawFees[$code] = $this->parse_transaction_fee($entry, $currency);
            }
        }
        return array(
            'withdraw' => $withdrawFees,
            'deposit' => array(),
            'info' => $response,
        );
    }

    public function parse_transaction_fee($transaction, ?array $currency = null) {
        //
        //    {
        //        "coin" => "AGLD",
        //        "name" => "Adventure Gold",
        //        "networkList" => array(
        //            {
        //                "coin" => "AGLD",
        //                "depositDesc" => null,
        //                "depositEnable" => true,
        //                "minConfirm" => "0",
        //                "name" => "Adventure Gold",
        //                "network" => "ERC20",
        //                "withdrawEnable" => true,
        //                "withdrawFee" => "10.000000000000000000",
        //                "withdrawIntegerMultiple" => null,
        //                "withdrawMax" => "1200000.000000000000000000",
        //                "withdrawMin" => "20.000000000000000000",
        //                "sameAddress" => false,
        //                "contract" => "0x32353a6c91143bfd6c7d363b546e62a9a2489a20",
        //                "withdrawTips" => null,
        //                "depositTips" => null
        //            }
        //            ...
        //        )
        //    }
        //
        $networkList = $this->safe_value($transaction, 'networkList', array());
        $result = array();
        for ($j = 0; $j < count($networkList); $j++) {
            $networkEntry = $networkList[$j];
            $networkId = $this->safe_string($networkEntry, 'network');
            $networkCode = $this->safe_string($this->options['networks'], $networkId, $networkId);
            $fee = $this->safe_number($networkEntry, 'withdrawFee');
            $result[$networkCode] = $fee;
        }
        return $result;
    }

    public function fetch_deposit_withdraw_fees(?array $codes = null, $params = array ()) {
        return Async\async(function () use ($codes, $params) {
            /**
             * fetch deposit and withdrawal fees
             *
             * @see https://mexcdevelop.github.io/apidocs/spot_v3_en/#query-the-currency-information
             *
             * @param {string[]|null} $codes returns fees for all currencies if null
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=fee-structure fee structures~
             */
            Async\await($this->load_markets());
            $response = Async\await($this->spotPrivateGetCapitalConfigGetall ($params));
            //
            //    array(
            //       {
            //           "coin" => "AGLD",
            //           "name" => "Adventure Gold",
            //           "networkList" => array(
            //               array(
            //                   "coin" => "AGLD",
            //                   "depositDesc" => null,
            //                   "depositEnable" => true,
            //                   "minConfirm" => "0",
            //                   "name" => "Adventure Gold",
            //                   "network" => "ERC20",
            //                   "withdrawEnable" => true,
            //                   "withdrawFee" => "10.000000000000000000",
            //                   "withdrawIntegerMultiple" => null,
            //                   "withdrawMax" => "1200000.000000000000000000",
            //                   "withdrawMin" => "20.000000000000000000",
            //                   "sameAddress" => false,
            //                   "contract" => "0x32353a6c91143bfd6c7d363b546e62a9a2489a20",
            //                   "withdrawTips" => null,
            //                   "depositTips" => null
            //               }
            //               ...
            //           )
            //       ),
            //       ...
            //    )
            //
            return $this->parse_deposit_withdraw_fees($response, $codes, 'coin');
        }) ();
    }

    public function parse_deposit_withdraw_fee($fee, ?array $currency = null) {
        //
        //    {
        //        "coin" => "AGLD",
        //        "name" => "Adventure Gold",
        //        "networkList" => array(
        //            {
        //                "coin" => "AGLD",
        //                "depositDesc" => null,
        //                "depositEnable" => true,
        //                "minConfirm" => "0",
        //                "name" => "Adventure Gold",
        //                "network" => "ERC20",
        //                "withdrawEnable" => true,
        //                "withdrawFee" => "10.000000000000000000",
        //                "withdrawIntegerMultiple" => null,
        //                "withdrawMax" => "1200000.000000000000000000",
        //                "withdrawMin" => "20.000000000000000000",
        //                "sameAddress" => false,
        //                "contract" => "0x32353a6c91143bfd6c7d363b546e62a9a2489a20",
        //                "withdrawTips" => null,
        //                "depositTips" => null
        //            }
        //            ...
        //        )
        //    }
        //
        $networkList = $this->safe_value($fee, 'networkList', array());
        $result = $this->deposit_withdraw_fee($fee);
        for ($j = 0; $j < count($networkList); $j++) {
            $networkEntry = $networkList[$j];
            $networkId = $this->safe_string($networkEntry, 'network');
            $networkCode = $this->network_id_to_code($networkId, $this->safe_string($currency, 'code'));
            $result['networks'][$networkCode] = array(
                'withdraw' => array(
                    'fee' => $this->safe_number($networkEntry, 'withdrawFee'),
                    'percentage' => null,
                ),
                'deposit' => array(
                    'fee' => null,
                    'percentage' => null,
                ),
            );
        }
        return $this->assign_default_deposit_withdraw_fees($result);
    }

    public function fetch_leverage(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the set leverage for a $market
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-leverage
             *
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=leverage-structure leverage structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'symbol' => $market['id'],
            );
            $response = Async\await($this->contractPrivateGetPositionLeverage ($this->extend($request, $params)));
            //
            //     {
            //         "success" => true,
            //         "code" => 0,
            //         "data" => array(
            //             array(
            //                 "level" => 1,
            //                 "maxVol" => 463300,
            //                 "mmr" => 0.004,
            //                 "imr" => 0.005,
            //                 "positionType" => 1,
            //                 "openType" => 1,
            //                 "leverage" => 20,
            //                 "limitBySys" => false,
            //                 "currentMmr" => 0.004
            //             ),
            //             {
            //                 "level" => 1,
            //                 "maxVol" => 463300,
            //                 "mmr" => 0.004,
            //                 "imr" => 0.005,
            //                 "positionType" => 2,
            //                 "openType" => 1,
            //                 "leverage" => 20,
            //                 "limitBySys" => false,
            //                 "currentMmr" => 0.004
            //             }
            //         )
            //     }
            //
            $data = $this->safe_list($response, 'data', array());
            return $this->parse_leverage($data, $market);
        }) ();
    }

    public function parse_leverage(array $leverage, ?array $market = null): array {
        $marginMode = null;
        $longLeverage = null;
        $shortLeverage = null;
        for ($i = 0; $i < count($leverage); $i++) {
            $entry = $leverage[$i];
            $openType = $this->safe_integer($entry, 'openType');
            $positionType = $this->safe_integer($entry, 'positionType');
            if ($positionType === 1) {
                $longLeverage = $this->safe_integer($entry, 'leverage');
            } elseif ($positionType === 2) {
                $shortLeverage = $this->safe_integer($entry, 'leverage');
            }
            $marginMode = ($openType === 1) ? 'isolated' : 'cross';
        }
        return array(
            'info' => $leverage,
            'symbol' => $market['symbol'],
            'marginMode' => $marginMode,
            'longLeverage' => $longLeverage,
            'shortLeverage' => $shortLeverage,
        );
    }

    public function handle_margin_mode_and_params($methodName, $params = array (), $defaultValue = null) {
        /**
         * @ignore
         * $marginMode specified by $params["marginMode"], $this->options["marginMode"], $this->options["defaultMarginMode"], $params["margin"] = true or $this->options["defaultType"] = 'margin'
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {bool} [$params->margin] true for trading spot-margin
         * @return {Array} the $marginMode in lowercase
         */
        $defaultType = $this->safe_string($this->options, 'defaultType');
        $isMargin = $this->safe_bool($params, 'margin', false);
        $marginMode = null;
        list($marginMode, $params) = parent::handle_margin_mode_and_params($methodName, $params, $defaultValue);
        if (($defaultType === 'margin') || ($isMargin === true)) {
            $marginMode = 'isolated';
        }
        return array( $marginMode, $params );
    }

    public function fetch_positions_history(?array $symbols = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $since, $limit, $params) {
            /**
             * fetches historical $positions
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#get-the-user-s-history-position-information
             *
             * @param {string[]} [$symbols] unified contract $symbols
             * @param {int} [$since] not used by mexc fetchPositionsHistory
             * @param {int} [$limit] the maximum amount of candles to fetch, default=1000
             * @param {array} [$params] extra parameters specific to the exchange api endpoint
             *
             * EXCHANGE SPECIFIC PARAMETERS
             * @param {int} [$params->type] position type，1 => long, 2 => short
             * @param {int} [$params->page_num] current page number, default is 1
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structures~
             */
            Async\await($this->load_markets());
            $request = array();
            if ($symbols !== null) {
                $symbolsLength = count($symbols);
                if ($symbolsLength === 1) {
                    $market = $this->market($symbols[0]);
                    $request['symbol'] = $market['id'];
                }
            }
            if ($limit !== null) {
                $request['page_size'] = $limit;
            }
            $response = Async\await($this->contractPrivateGetPositionListHistoryPositions ($this->extend($request, $params)));
            //
            //    {
            //        success => true,
            //        code => '0',
            //        $data => array(
            //            array(
            //                positionId => '390281084',
            //                symbol => 'RVN_USDT',
            //                positionType => '1',
            //                openType => '2',
            //                state => '3',
            //                holdVol => '0',
            //                frozenVol => '0',
            //                closeVol => '1141',
            //                holdAvgPrice => '0.03491',
            //                holdAvgPriceFullyScale => '0.03491',
            //                openAvgPrice => '0.03491',
            //                openAvgPriceFullyScale => '0.03491',
            //                closeAvgPrice => '0.03494',
            //                liquidatePrice => '0.03433',
            //                oim => '0',
            //                im => '0',
            //                holdFee => '0',
            //                realised => '0.1829',
            //                leverage => '50',
            //                createTime => '1711512408000',
            //                updateTime => '1711512553000',
            //                autoAddIm => false,
            //                version => '4',
            //                profitRatio => '0.0227',
            //                newOpenAvgPrice => '0.03491',
            //                newCloseAvgPrice => '0.03494',
            //                closeProfitLoss => '0.3423',
            //                fee => '0.1593977',
            //                positionShowStatus => 'CLOSED'
            //            ),
            //            ...
            //        )
            //    }
            //
            $data = $this->safe_list($response, 'data');
            $positions = $this->parse_positions($data, $symbols, $params);
            return $this->filter_by_since_limit($positions, $since, $limit);
        }) ();
    }

    public function set_margin_mode(string $marginMode, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($marginMode, $symbol, $params) {
            /**
             * set margin mode to 'cross' or 'isolated'
             *
             * @see https://mexcdevelop.github.io/apidocs/contract_v1_en/#switch-$leverage
             *
             * @param {string} $marginMode 'cross' or 'isolated'
             * @param {string} [$symbol] required when there is no position, else provide $params["positionId"]
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->positionId] required when a position is set
             * @param {string} [$params->direction] "long" or "short" required when there is no position
             * @return {array} $response from the exchange
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if ($market['spot']) {
                throw new BadSymbol($this->id . ' setMarginMode() supports contract markets only');
            }
            $marginMode = strtolower($marginMode);
            if ($marginMode !== 'isolated' && $marginMode !== 'cross') {
                throw new BadRequest($this->id . ' setMarginMode() $marginMode argument should be isolated or cross');
            }
            $leverage = $this->safe_integer($params, 'leverage');
            if ($leverage === null) {
                throw new ArgumentsRequired($this->id . ' setMarginMode() requires a $leverage parameter');
            }
            $direction = $this->safe_string_lower_2($params, 'direction', 'positionId');
            $request = array(
                'leverage' => $leverage,
                'openType' => ($marginMode === 'isolated') ? 1 : 2,
            );
            if ($symbol !== null) {
                $request['symbol'] = $market['id'];
            }
            if ($direction !== null) {
                $request['positionType'] = ($direction === 'short') ? 2 : 1;
            }
            $params = $this->omit($params, 'direction');
            $response = Async\await($this->contractPrivatePostPositionChangeLeverage ($this->extend($request, $params)));
            //
            // array( success => true, code => '0' )
            //
            return $this->parse_leverage($response, $market); // tmp revert type
        }) ();
    }

    public function nonce() {
        return $this->milliseconds() - $this->safe_integer($this->options, 'timeDifference', 0);
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $section = $this->safe_string($api, 0);
        $access = $this->safe_string($api, 1);
        list($path, $params) = $this->resolve_path($path, $params);
        $url = null;
        if ($section === 'spot' || $section === 'broker') {
            if ($section === 'broker') {
                $url = $this->urls['api'][$section][$access] . '/' . $path;
            } else {
                $url = $this->urls['api'][$section][$access] . '/api/' . $this->version . '/' . $path;
            }
            $urlParams = $params;
            if ($access === 'private') {
                if ($section === 'broker' && (($method === 'POST') || ($method === 'PUT') || ($method === 'DELETE'))) {
                    $urlParams = array(
                        'timestamp' => $this->nonce(),
                        'recvWindow' => $this->safe_integer($this->options, 'recvWindow', 5000),
                    );
                    $body = $this->json($params);
                } else {
                    $urlParams['timestamp'] = $this->nonce();
                    $urlParams['recvWindow'] = $this->safe_integer($this->options, 'recvWindow', 5000);
                }
            }
            $paramsEncoded = '';
            if ($urlParams) {
                $paramsEncoded = $this->urlencode($urlParams);
                $url .= '?' . $paramsEncoded;
            }
            if ($access === 'private') {
                $this->check_required_credentials();
                $signature = $this->hmac($this->encode($paramsEncoded), $this->encode($this->secret), 'sha256');
                $url .= '&' . 'signature=' . $signature;
                $headers = array(
                    'X-MEXC-APIKEY' => $this->apiKey,
                    'source' => $this->safe_string($this->options, 'broker', 'CCXT'),
                );
            }
            if (($method === 'POST') || ($method === 'PUT') || ($method === 'DELETE')) {
                $headers['Content-Type'] = 'application/json';
            }
        } elseif ($section === 'contract' || $section === 'spot2') {
            $url = $this->urls['api'][$section][$access] . '/' . $this->implode_params($path, $params);
            $params = $this->omit($params, $this->extract_params($path));
            if ($access === 'public') {
                if ($params) {
                    $url .= '?' . $this->urlencode($params);
                }
            } else {
                $this->check_required_credentials();
                $timestamp = (string) $this->nonce();
                $auth = '';
                $headers = array(
                    'ApiKey' => $this->apiKey,
                    'Request-Time' => $timestamp,
                    'Content-Type' => 'application/json',
                    'source' => $this->safe_string($this->options, 'broker', 'CCXT'),
                );
                if ($method === 'POST') {
                    $auth = $this->json($params);
                    $body = $auth;
                } else {
                    $params = $this->keysort($params);
                    if ($params) {
                        $auth .= $this->urlencode($params);
                        $url .= '?' . $auth;
                    }
                }
                $auth = $this->apiKey . $timestamp . $auth;
                $signature = $this->hmac($this->encode($auth), $this->encode($this->secret), 'sha256');
                $headers['Signature'] = $signature;
            }
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function handle_errors(int $code, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) {
        if ($response === null) {
            return null;
        }
        // spot
        //     array("code":-1128,"msg":"Combination of optional parameters invalid.","_extend":null)
        //     array("success":false,"code":123456,"message":"Order quantity error....")
        //
        // contract
        //
        //     array("code":10232,"msg":"The currency not exist")
        //     array("code":10216,"msg":"No available deposit address")
        //     array("success":true, "code":0, "data":1634095541710)
        //
        $success = $this->safe_bool($response, 'success', false); // v1
        if ($success === true) {
            return null;
        }
        $responseCode = $this->safe_string($response, 'code', null);
        if (($responseCode !== null) && ($responseCode !== '200') && ($responseCode !== '0')) {
            $feedback = $this->id . ' ' . $body;
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $body, $feedback);
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $responseCode, $feedback);
            throw new ExchangeError($feedback);
        }
        return null;
    }
}
