<?php

namespace ccxt;

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

use Exception; // a common import
use ccxt\abstract\defx as Exchange;

class defx extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'defx',
            'name' => 'Defx X',
            // 'countries' => array( '' ),
            'rateLimit' => 100,
            'version' => 'v1',
            'certified' => false,
            'pro' => false,
            'hostname' => 'defx.com',
            'dex' => true,
            'has' => array(
                'CORS' => null,
                'spot' => false,
                'margin' => false,
                'swap' => true,
                'future' => false,
                'option' => false,
                'addMargin' => true,
                'cancelAllOrders' => true,
                'cancelAllOrdersAfter' => false,
                'cancelOrder' => true,
                'cancelWithdraw' => false,
                'closeAllPositions' => true,
                'closePosition' => true,
                'createConvertTrade' => false,
                'createDepositAddress' => false,
                'createMarketBuyOrderWithCost' => false,
                'createMarketOrder' => false,
                'createMarketOrderWithCost' => false,
                'createMarketSellOrderWithCost' => false,
                'createOrder' => true,
                'createOrderWithTakeProfitAndStopLoss' => true,
                'createReduceOnlyOrder' => true,
                'createStopLimitOrder' => false,
                'createStopLossOrder' => false,
                'createStopMarketOrder' => false,
                'createStopOrder' => false,
                'createTakeProfitOrder' => true,
                'createTrailingAmountOrder' => false,
                'createTrailingPercentOrder' => false,
                'createTriggerOrder' => true,
                'fetchAccounts' => false,
                'fetchBalance' => true,
                'fetchCanceledOrders' => true,
                'fetchClosedOrder' => false,
                'fetchClosedOrders' => true,
                'fetchConvertCurrencies' => false,
                'fetchConvertQuote' => false,
                'fetchConvertTrade' => false,
                'fetchConvertTradeHistory' => false,
                'fetchCurrencies' => false,
                'fetchDepositAddress' => false,
                'fetchDepositAddresses' => false,
                'fetchDepositAddressesByNetwork' => false,
                'fetchDeposits' => false,
                'fetchDepositsWithdrawals' => false,
                'fetchFundingHistory' => false,
                'fetchFundingInterval' => false,
                'fetchFundingIntervals' => false,
                'fetchFundingRate' => true,
                'fetchFundingRateHistory' => false,
                'fetchFundingRates' => false,
                'fetchIndexOHLCV' => false,
                'fetchLedger' => true,
                'fetchLeverage' => false,
                'fetchMarginAdjustmentHistory' => false,
                'fetchMarginMode' => false,
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => false,
                'fetchMarkPrice' => false,
                'fetchMarkPrices' => false,
                'fetchMyTrades' => true,
                'fetchOHLCV' => true,
                'fetchOpenInterestHistory' => false,
                'fetchOpenOrder' => false,
                'fetchOpenOrders' => true,
                'fetchOrder' => true,
                'fetchOrderBook' => true,
                'fetchOrders' => true,
                'fetchOrderTrades' => false,
                'fetchPosition' => true,
                'fetchPositionHistory' => false,
                'fetchPositionMode' => false,
                'fetchPositions' => true,
                'fetchPositionsHistory' => false,
                'fetchPremiumIndexOHLCV' => false,
                'fetchStatus' => true,
                'fetchTicker' => true,
                'fetchTickers' => true,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => false,
                'fetchTradingFees' => false,
                'fetchTransactions' => false,
                'fetchTransfers' => false,
                'fetchWithdrawals' => false,
                'reduceMargin' => false,
                'sandbox' => true,
                'setLeverage' => true,
                'setMargin' => false,
                'setPositionMode' => false,
                'transfer' => false,
                'withdraw' => true,
            ),
            'timeframes' => array(
                '1m' => '1m',
                '3m' => '3m',
                '5m' => '5m',
                '15m' => '15m',
                '30m' => '30m',
                '1h' => '1h',
                '2h' => '2h',
                '4h' => '4h',
                '12h' => '12h',
                '1d' => '1d',
                '1w' => '1w',
                '1M' => '1M',
            ),
            'urls' => array(
                'logo' => 'https://github.com/user-attachments/assets/4e92bace-d7a9-45ea-92be-122168dc87e4',
                'api' => array(
                    'public' => 'https://api.{hostname}',
                    'private' => 'https://api.{hostname}',
                ),
                'test' => array(
                    'public' => 'https://api.testnet.{hostname}',
                    'private' => 'https://api.testnet.{hostname}',
                ),
                'www' => 'https://defx.com/home',
                'doc' => array(
                    'https://docs.defx.com/docs',
                    'https://api-docs.defx.com/',
                ),
                'fees' => array(
                    '',
                ),
                'referral' => array(
                    'url' => 'https://app.defx.com/join/6I2CZ7',
                ),
            ),
            'api' => array(
                'v1' => array(
                    'public' => array(
                        'get' => array(
                            'healthcheck/ping' => 1,
                            'symbols/{symbol}/ohlc' => 1,
                            'symbols/{symbol}/trades' => 1,
                            'symbols/{symbol}/prices' => 1,
                            'symbols/{symbol}/ticker/24hr' => 1,
                            'symbols/{symbol}/depth/{level}/{slab}' => 1,
                            'ticker/24HrAgg' => 1,
                            'c/markets' => 1,
                            'c/markets/metadata' => 1,
                            'analytics/market/stats/newUsers' => 1,
                            'analytics/market/stats/tvl' => 1,
                            'analytics/market/stats/volumeByInstrument' => 1,
                            'analytics/market/stats/liquidation' => 1,
                            'analytics/market/stats/totalVolume' => 1,
                            'analytics/market/stats/openInterest' => 1,
                            'analytics/market/stats/totalTrades' => 1,
                            'analytics/market/stats/basis' => 1,
                            'analytics/market/stats/insuranceFund' => 1,
                            'analytics/market/stats/longAndShortRatio' => 1,
                            'analytics/market/stats/fundingRate' => 1,
                            'analytics/market/overview' => 1,
                            'explorer/search' => 1,
                            'explorer/transactions' => 1,
                            'explorer/blocks' => 1,
                        ),
                    ),
                    'private' => array(
                        'get' => array(
                            'api/order/{orderId}' => 1,
                            'api/orders' => 1,
                            'api/orders/oco/{parentOrderId}' => 1,
                            'api/trades' => 1,
                            'api/position/active' => 1,
                            'api/users/metadata/leverage' => 1,
                            'api/users/metadata/feeMultiplier' => 1,
                            'api/users/metadata/slippage' => 1,
                            'api/users/referral' => 1,
                            'api/users/apikeys' => 1,
                            'connection-signature-message/evm' => 1,
                            'api/users/profile/wallets' => 1,
                            'api/notifications' => 1,
                            'api/wallet/balance' => 1,
                            'api/wallet/transactions' => 1,
                            'api/analytics/user/overview' => 1,
                            'api/analytics/user/pnl' => 1,
                            'api/analytics/points/overview' => 1,
                            'api/analytics/points/history' => 1,
                        ),
                        'post' => array(
                            'api/order' => 1,
                            'api/position/oco' => 1,
                            'api/users/socket/listenKeys' => 1,
                            'api/users/metadata/leverage' => 1,
                            'api/users/metadata/feeMultiplier' => 1,
                            'api/users/metadata/slippage' => 1,
                            'api/users/referral/recordReferralSignup' => 1,
                            'api/users/apikeys' => 1,
                            'api/users/profile/wallets' => 1,
                            'api/transfers/withdrawal' => 1,
                            'api/transfers/bridge/withdrawal' => 1,
                        ),
                        'put' => array(
                            'api/position/updatePositionMargin' => 1,
                            'api/users/socket/listenKeys/{listenKey}' => 1,
                            'api/users/apikeys/{accessKey}/status' => 1,
                            'api/users/referral' => 1,
                        ),
                        'patch' => array(
                            'api/users/apikeys/{accessKey}' => 1,
                        ),
                        'delete' => array(
                            'api/orders/allOpen' => 1,
                            'api/order/{orderId}' => 1,
                            'api/position/{positionId}' => 1,
                            'api/position/all' => 1,
                            'api/users/socket/listenKeys/{listenKey}' => 1,
                            'api/users/apikeys/{accessKey}' => 1,
                        ),
                    ),
                ),
            ),
            'fees' => array(
                'trading' => array(
                    'tierBased' => true,
                    'percentage' => true,
                    'maker' => $this->parse_number('0.0002'),
                    'taker' => $this->parse_number('0.0005'),
                ),
            ),
            'options' => array(
                'sandboxMode' => false,
            ),
            'features' => array(
                'spot' => null,
                'forDerivatives' => array(
                    'sandbox' => true,
                    'createOrder' => array(
                        'marginMode' => false,
                        'triggerPrice' => true,
                        // todo implement
                        'triggerPriceType' => array(
                            'last' => true,
                            'mark' => true,
                            'index' => false,
                        ),
                        'triggerDirection' => false,
                        'stopLossPrice' => false, // todo
                        'takeProfitPrice' => false, // todo
                        'attachedStopLossTakeProfit' => null,
                        'timeInForce' => array(
                            'IOC' => true,
                            'FOK' => true,
                            'PO' => true,
                            'GTD' => false,
                        ),
                        'hedged' => false,
                        'selfTradePrevention' => false,
                        'trailing' => false,
                        'iceberg' => false,
                        'leverage' => false,
                        'marketBuyByCost' => false,
                        'marketBuyRequiresPrice' => false,
                    ),
                    'createOrders' => null,
                    'fetchMyTrades' => array(
                        'marginMode' => false,
                        'limit' => 1000,
                        'daysBack' => null,
                        'untilDays' => null,
                        'symbolRequired' => false,
                    ),
                    'fetchOrder' => array(
                        'marginMode' => false,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOpenOrders' => array(
                        'marginMode' => true,
                        'limit' => 100,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOrders' => array(
                        'marginMode' => false,
                        'limit' => 500,
                        'daysBack' => 100000,
                        'untilDays' => 100000,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchClosedOrders' => array(
                        'marginMode' => false,
                        'limit' => 500,
                        'daysBack' => 100000,
                        'daysBackCanceled' => 1,
                        'untilDays' => 100000,
                        'trigger' => false,
                        'trailing' => false,
                        'symbolRequired' => false,
                    ),
                    'fetchOHLCV' => array(
                        'limit' => 1000,
                    ),
                ),
                'swap' => array(
                    'linear' => array(
                        'extends' => 'forDerivatives',
                    ),
                    'inverse' => null,
                ),
                'future' => array(
                    'linear' => null,
                    'inverse' => null,
                ),
            ),
            'commonCurrencies' => array(),
            'exceptions' => array(
                'exact' => array(
                    '404' => '\\ccxt\\BadRequest', // array("errorCode":404,"errorMessage":"Not Found")
                    'missing_auth_signature' => '\\ccxt\\AuthenticationError', // array("msg":"Missing auth signature","code":"missing_auth_signature")
                    'order_rejected' => '\\ccxt\\InvalidOrder', // array("success":false,"err":array("msg":"Order has already been rejected","code":"order_rejected"))
                    'invalid_order_id' => '\\ccxt\\InvalidOrder', // array("success":false,"err":array("msg":"Invalid order id","code":"invalid_order_id"))
                    'filter_lotsize_maxqty' => '\\ccxt\\InvalidOrder', // array("errorCode":"filter_lotsize_maxqty","errorMessage":"LOT_SIZE filter failed, quantity more than maxQty","errorData":array("maxQty":"5000.00"))
                    'filter_notional_min' => '\\ccxt\\InvalidOrder', // array("errorCode":"filter_notional_min","errorMessage":"NOTIONAL filter failed, Notional value of quote asset less than minNotional","errorData":array("minNotional":"100.00000000"))
                    'failed_index_price_up_multiplier_filter' => '\\ccxt\\InvalidOrder', // array("errorCode":"failed_index_price_up_multiplier_filter","errorMessage":"failed_index_price_up_multiplier_filter","errorData":array("maxPrice":"307.81241042"))
                    'no_open_orders' => '\\ccxt\\InvalidOrder', // array("errorMessage":"No open orders found","errorCode":"no_open_orders")
                    'active_position_not_found' => '\\ccxt\\InvalidOrder', // array("errorCode":"active_position_not_found","errorMessage":"Active position not found")
                    'position_inactive' => '\\ccxt\\InvalidOrder', // array("errorCode":"position_inactive","errorMessage":"Position is already inactive")
                    'invalid_position_id' => '\\ccxt\\InvalidOrder', // array("errorCode":"invalid_position_id","errorMessage":"Position id is invalid")
                    'Internal server error' => '\\ccxt\\ExchangeError', // array("msg":"Internal server error","code":"internal_server_error")
                ),
                'broad' => array(
                    'Bad Request' => '\\ccxt\\BadRequest', // array("errorMessage":"Bad Request","data":[array("param":"symbol","message":"\"symbol\" must be one of [ETH_USDC, BTC_USDC, BNB_USDC, SOL_USDC, DOGE_USDC, TON_USDC, AVAX_USDC, WIF_USDC, KPEPE_USDC, KSHIB_USDC, KBONK_USDC, MOODENG_USDC, POPCAT_USDC, MOTHER_USDC]")])
                ),
            ),
            'precisionMode' => TICK_SIZE,
        ));
    }

    public function fetch_status($params = array ()) {
        /**
         * the latest known information on the availability of the exchange API
         *
         * @see https://api-docs.defx.com/#4b03bb3b-a0fa-4dfb-b96c-237bde0ce9e6
         *
         * @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~
         */
        $response = $this->v1PublicGetHealthcheckPing ($params);
        //
        // {
        //     "success" => true,
        //     "t" => 1709705048323,
        //     "v" => "0.0.7",
        //     "msg" => "A programmer’s wife tells him, “While you’re at the grocery store, buy some eggs.” He never comes back."
        // }
        //
        $status = null;
        $success = $this->safe_bool($response, 'success');
        if ($success) {
            $status = 'ok';
        } else {
            $status = 'error';
        }
        return array(
            'status' => $status,
            'updated' => null,
            'eta' => null,
            'url' => null,
            'info' => $response,
        );
    }

    public function fetch_time($params = array ()): ?int {
        /**
         * fetches the current integer timestamp in milliseconds from the exchange server
         *
         * @see https://api-docs.defx.com/#4b03bb3b-a0fa-4dfb-b96c-237bde0ce9e6
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {int} the current integer timestamp in milliseconds from the exchange server
         */
        $response = $this->v1PublicGetHealthcheckPing ($params);
        //
        // {
        //     "success" => true,
        //     "t" => 1709705048323,
        //     "v" => "0.0.7",
        //     "msg" => "A programmer’s wife tells him, “While you’re at the grocery store, buy some eggs.” He never comes back."
        // }
        //
        return $this->safe_integer($response, 't');
    }

    public function fetch_markets($params = array ()): array {
        /**
         * retrieves data on all markets for defx
         *
         * @see https://api-docs.defx.com/#73cce0c8-f842-4891-9145-01bb6d61324d
         * @see https://api-docs.defx.com/#24fd4e5b-840e-451e-99e0-7fea47c7f371
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} an array of objects representing market data
         */
        $request = array(
            'type' => 'perps',
        );
        $promises = array(
            $this->v1PublicGetCMarkets ($this->extend($request, $params)),
            $this->v1PublicGetCMarketsMetadata ($this->extend($request, $params)),
        );
        $responses = $promises;
        //
        // {
        //     "data" => array(
        //       {
        //         "market" => "DOGE_USDC",
        //         "candleWindows" => array(
        //           "1m",
        //           "3m",
        //           "5m",
        //           "15m",
        //           "30m",
        //           "1h",
        //           "2h",
        //           "4h",
        //           "12h",
        //           "1d",
        //           "1w",
        //           "1M"
        //         ),
        //         "depthSlabs" => array(
        //           "0.00001",
        //           "0.00005",
        //           "0.0001",
        //           "0.001",
        //           "0.01"
        //         ),
        //         "filters" => array(
        //           array(
        //             "filterType" => "LOT_SIZE",
        //             "minQty" => "1.00000",
        //             "maxQty" => "1500000.00000",
        //             "stepSize" => "1.00000"
        //           ),
        //           array(
        //             "filterType" => "MARKET_LOT_SIZE",
        //             "minQty" => "1.00000",
        //             "maxQty" => "750000.00000",
        //             "stepSize" => "1.00000"
        //           ),
        //           array(
        //             "filterType" => "PRICE_FILTER",
        //             "minPrice" => "0.00244000",
        //             "maxPrice" => "30.00000000",
        //             "tickSize" => "0.00001"
        //           ),
        //           array(
        //             "filterType" => "NOTIONAL",
        //             "minNotional" => "100.00000000"
        //           ),
        //           array(
        //             "filterType" => "PERCENT_PRICE_BY_SIDE",
        //             "bidMultiplierUp" => "1.5",
        //             "bidMultiplierDown" => "0.5",
        //             "askMultiplierUp" => "1.5",
        //             "askMultiplierDown" => "0.5"
        //           ),
        //           {
        //             "filterType" => "INDEX_PRICE_FILTER",
        //             "multiplierUp" => "1.3",
        //             "multiplierDown" => "0.7"
        //           }
        //         ),
        //         "cappedLeverage" => "25",
        //         "maintenanceMarginTiers" => array(
        //           array(
        //             "tier" => "1",
        //             "minMaintenanceMargin" => "0",
        //             "maxMaintenanceMargin" => "2500",
        //             "leverage" => "25"
        //           ),
        //           array(
        //             "tier" => "2",
        //             "minMaintenanceMargin" => "2500",
        //             "maxMaintenanceMargin" => "12500",
        //             "leverage" => "20"
        //           ),
        //           array(
        //             "tier" => "3",
        //             "minMaintenanceMargin" => "12500",
        //             "maxMaintenanceMargin" => "25000",
        //             "leverage" => "15"
        //           ),
        //           array(
        //             "tier" => "4",
        //             "minMaintenanceMargin" => "25000",
        //             "maxMaintenanceMargin" => "50000",
        //             "leverage" => "10"
        //           ),
        //           array(
        //             "tier" => "5",
        //             "minMaintenanceMargin" => "50000",
        //             "maxMaintenanceMargin" => "75000",
        //             "leverage" => "8"
        //           ),
        //           array(
        //             "tier" => "6",
        //             "minMaintenanceMargin" => "75000",
        //             "maxMaintenanceMargin" => "125000",
        //             "leverage" => "7"
        //           ),
        //           array(
        //             "tier" => "7",
        //             "minMaintenanceMargin" => "125000",
        //             "maxMaintenanceMargin" => "187500",
        //             "leverage" => "5"
        //           ),
        //           array(
        //             "tier" => "8",
        //             "minMaintenanceMargin" => "187500",
        //             "maxMaintenanceMargin" => "250000",
        //             "leverage" => "3"
        //           ),
        //           array(
        //             "tier" => "9",
        //             "minMaintenanceMargin" => "250000",
        //             "maxMaintenanceMargin" => "375000",
        //             "leverage" => "2"
        //           ),
        //           {
        //             "tier" => "10",
        //             "minMaintenanceMargin" => "375000",
        //             "maxMaintenanceMargin" => "500000",
        //             "leverage" => "1"
        //           }
        //         ),
        //         "fees" => array(
        //           "maker" => "0.08",
        //           "taker" => "0.1"
        //         }
        //       ),
        //     )
        // }
        //
        $activeMarkets = $this->safe_list($responses[0], 'data');
        $activeMarketsByType = $this->index_by($activeMarkets, 'market');
        $marketMetadatas = $this->safe_list($responses[1], 'data');
        for ($i = 0; $i < count($marketMetadatas); $i++) {
            $marketId = $marketMetadatas[$i]['market'];
            $status = null;
            if (is_array($activeMarketsByType) && array_key_exists($marketId, $activeMarketsByType)) {
                $status = $activeMarketsByType[$marketId]['status'];
            }
            $marketMetadatas[$i]['status'] = $status;
        }
        return $this->parse_markets($marketMetadatas);
    }

    public function parse_market(array $market): array {
        $marketId = $this->safe_string($market, 'market');
        $parts = explode('_', $marketId);
        $baseId = $this->safe_string($parts, 0);
        $quoteId = $this->safe_string($parts, 1);
        $base = $this->safe_currency_code($baseId);
        $quote = $this->safe_currency_code($quoteId);
        $symbol = $base . '/' . $quote . ':' . $quote;
        $filters = $this->safe_list($market, 'filters', array());
        $fees = $this->safe_dict($market, 'fees', array());
        $filtersByType = $this->index_by($filters, 'filterType');
        $priceFilter = $this->safe_dict($filtersByType, 'PRICE_FILTER', array());
        $lotFilter = $this->safe_dict($filtersByType, 'LOT_SIZE', array());
        $marketLotFilter = $this->safe_dict($filtersByType, 'MARKET_LOT_SIZE', array());
        $notionalFilter = $this->safe_dict($filtersByType, 'NOTIONAL', array());
        return array(
            'id' => $marketId,
            'symbol' => $symbol,
            'base' => $base,
            'quote' => $quote,
            'settle' => $quote,
            'baseId' => $baseId,
            'quoteId' => $quoteId,
            'settleId' => $quoteId,
            'type' => 'swap',
            'spot' => false,
            'margin' => false,
            'swap' => true,
            'future' => false,
            'option' => false,
            'active' => $this->safe_string($market, 'status', '') === 'active',
            'contract' => true,
            'linear' => true,
            'inverse' => false,
            'taker' => $this->safe_number($fees, 'taker'),
            'maker' => $this->safe_number($fees, 'maker'),
            'contractSize' => $this->parse_number('1'),
            'expiry' => null,
            'expiryDatetime' => null,
            'strike' => null,
            'optionType' => null,
            'precision' => array(
                'amount' => $this->safe_number($lotFilter, 'stepSize'),
                'price' => $this->safe_number($priceFilter, 'tickSize'),
            ),
            'limits' => array(
                'leverage' => array(
                    'min' => null,
                    'max' => $this->safe_number($market, 'cappedLeverage'),
                ),
                'amount' => array(
                    'min' => $this->safe_number($lotFilter, 'minQty'),
                    'max' => $this->safe_number($lotFilter, 'maxQty'),
                ),
                'price' => array(
                    'min' => $this->safe_number($priceFilter, 'minPrice'),
                    'max' => $this->safe_number($priceFilter, 'maxPrice'),
                ),
                'cost' => array(
                    'min' => $this->safe_number($notionalFilter, 'minNotional'),
                    'max' => null,
                ),
                'market' => array(
                    'min' => $this->safe_number($marketLotFilter, 'minQty'),
                    'max' => $this->safe_number($marketLotFilter, 'maxQty'),
                ),
            ),
            'created' => null,
            'info' => $market,
        );
    }

    public function fetch_ticker(string $symbol, $params = array ()): array {
        /**
         * fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market
         *
         * @see https://api-docs.defx.com/#fe6f81d0-2f3a-4eee-976f-c8fc8f4c5d56
         *
         * @param {string} $symbol unified $symbol of the $market to fetch the ticker for
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = $this->v1PublicGetSymbolsSymbolTicker24hr ($this->extend($request, $params));
        //
        // {
        //     "symbol" => "BTC_USDC",
        //     "priceChange" => "0",
        //     "priceChangePercent" => "0",
        //     "weightedAvgPrice" => "0",
        //     "lastPrice" => "2.00",
        //     "lastQty" => "10.000",
        //     "bestBidPrice" => "1646.00",
        //     "bestBidQty" => "10.000",
        //     "bestAskPrice" => "1646.00",
        //     "bestAskQty" => "10.000",
        //     "openPrice" => "0.00",
        //     "highPrice" => "0.00",
        //     "lowPrice" => "0.00",
        //     "volume" => "0.000",
        //     "quoteVolume" => "0.00",
        //     "openTime" => 1700142658697,
        //     "closeTime" => 1700142658697,
        //     "openInterestBase" => "1.000",
        //     "openInterestQuote" => "0.43112300"
        // }
        //
        return $this->parse_ticker($response, $market);
    }

    public function fetch_tickers(?array $symbols = null, $params = array ()): array {
        /**
         * fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each $market
         *
         * @see https://api-docs.defx.com/#8c61cfbd-40d9-410e-b014-f5b36eba51d1
         *
         * @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~
         */
        $this->load_markets();
        $market = null;
        if ($symbols !== null) {
            $symbols = $this->market_symbols($symbols);
            $firstSymbol = $this->safe_string($symbols, 0);
            if ($firstSymbol !== null) {
                $market = $this->market($firstSymbol);
            }
        }
        $type = null;
        list($type, $params) = $this->handle_market_type_and_params('fetchTickers', $market, $params);
        if ($type === 'spot') {
            throw new NotSupported($this->id . ' fetchTickers() is not supported for ' . $type . ' markets');
        }
        $response = $this->v1PublicGetTicker24HrAgg ($params);
        //
        // {
        //     "ETH_USDC" => {
        //       "priceChange" => "0",
        //       "priceChangePercent" => "0",
        //       "openPrice" => "1646.15",
        //       "highPrice" => "1646.15",
        //       "lowPrice" => "1646.15",
        //       "lastPrice" => "1646.15",
        //       "quoteVolume" => "13.17",
        //       "volume" => "0.008",
        //       "markPrice" => "1645.15"
        //     }
        // }
        //
        return $this->parse_tickers($response, $symbols);
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        //
        // fetchTicker
        //
        // {
        //     "symbol" => "BTC_USDC",
        //     "priceChange" => "0",
        //     "priceChangePercent" => "0",
        //     "weightedAvgPrice" => "0",
        //     "lastPrice" => "2.00",
        //     "lastQty" => "10.000",
        //     "bestBidPrice" => "1646.00",
        //     "bestBidQty" => "10.000",
        //     "bestAskPrice" => "1646.00",
        //     "bestAskQty" => "10.000",
        //     "openPrice" => "0.00",
        //     "highPrice" => "0.00",
        //     "lowPrice" => "0.00",
        //     "volume" => "0.000",
        //     "quoteVolume" => "0.00",
        //     "openTime" => 1700142658697,
        //     "closeTime" => 1700142658697,
        //     "openInterestBase" => "1.000",
        //     "openInterestQuote" => "0.43112300"
        // }
        //
        // fetchTickers
        //
        //     "ETH_USDC" => {
        //       "priceChange" => "0",
        //       "priceChangePercent" => "0",
        //       "openPrice" => "1646.15",
        //       "highPrice" => "1646.15",
        //       "lowPrice" => "1646.15",
        //       "lastPrice" => "1646.15",
        //       "quoteVolume" => "13.17",
        //       "volume" => "0.008",
        //       "markPrice" => "1645.15"
        //     }
        //
        // fetchMarkPrice
        //
        // {
        //     "markPrice" => "100.00",
        //     "indexPrice" => "100.00",
        //     "ltp" => "101.34",
        //     "movingFundingRate" => "0.08",
        //     "payoutFundingRate" => "-0.03",
        //     "nextFundingPayout" => 1711555532146
        // }
        //
        $marketId = $this->safe_string($ticker, 'symbol');
        if ($marketId !== null) {
            $market = $this->market($marketId);
        }
        $symbol = $market['symbol'];
        $open = $this->safe_string($ticker, 'openPrice');
        $high = $this->safe_string($ticker, 'highPrice');
        $low = $this->safe_string($ticker, 'lowPrice');
        $close = $this->safe_string($ticker, 'lastPrice');
        $quoteVolume = $this->safe_string($ticker, 'quoteVolume');
        $baseVolume = $this->safe_string($ticker, 'volume');
        $percentage = $this->safe_string($ticker, 'priceChangePercent');
        $change = $this->safe_string($ticker, 'priceChange');
        $ts = $this->safe_integer($ticker, 'closeTime');
        if ($ts === 0) {
            $ts = null;
        }
        $datetime = $this->iso8601($ts);
        $bid = $this->safe_string($ticker, 'bestBidPrice');
        $bidVolume = $this->safe_string($ticker, 'bestBidQty');
        $ask = $this->safe_string($ticker, 'bestAskPrice');
        $askVolume = $this->safe_string($ticker, 'bestAskQty');
        return $this->safe_ticker(array(
            'symbol' => $symbol,
            'timestamp' => $ts,
            'datetime' => $datetime,
            'high' => $high,
            'low' => $low,
            'bid' => $bid,
            'bidVolume' => $bidVolume,
            'ask' => $ask,
            'askVolume' => $askVolume,
            'vwap' => null,
            'open' => $open,
            'close' => $close,
            'last' => null,
            'previousClose' => null,
            'change' => $change,
            'percentage' => $percentage,
            'average' => null,
            'baseVolume' => $baseVolume,
            'quoteVolume' => $quoteVolume,
            'markPrice' => $this->safe_string($ticker, 'markPrice'),
            'indexPrice' => $this->safe_string($ticker, 'indexPrice'),
            'info' => $ticker,
        ), $market);
    }

    public function fetch_ohlcv(string $symbol, $timeframe = '1m', ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         *
         * @see https://api-docs.defx.com/#54b71951-1472-4670-b5af-4c2dc41e73d0
         *
         * 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] max=1000, max=100 when $since is defined and is less than (now - (999 * (is_array(ms) && array_key_exists($timeframe, ms))))
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {int} [$params->until] the latest time in ms to fetch orders for
         * @return {int[][]} A list of candles ordered, open, high, low, close, volume
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $maxLimit = 1000;
        if ($limit === null) {
            $limit = $maxLimit;
        }
        $limit = min ($maxLimit, $limit);
        $request = array(
            'symbol' => $market['id'],
            'interval' => $this->safe_string($this->timeframes, $timeframe, $timeframe),
            'limit' => $limit,
        );
        $until = $this->safe_integer_2($params, 'until', 'till');
        $params = $this->omit($params, array( 'until', 'till' ));
        $request['endTime'] = ($until === null) ? $this->milliseconds() : $until;
        if ($since === null) {
            $request['startTime'] = 0;
        } else {
            $request['startTime'] = $since;
            if ($until === null) {
                $timeframeInSeconds = $this->parse_timeframe($timeframe);
                $timeframeInMilliseconds = $timeframeInSeconds * 1000;
                $totalTimeframeInMilliseconds = $limit * $timeframeInMilliseconds;
                $request['endTime'] = $this->sum($since, $totalTimeframeInMilliseconds);
            }
        }
        $response = $this->v1PublicGetSymbolsSymbolOhlc ($this->extend($request, $params));
        //
        // array(
        //     {
        //       "symbol" => "BTC_USDC",
        //       "open" => "0.00",
        //       "high" => "0.00",
        //       "low" => "0.00",
        //       "close" => "0.00",
        //       "volume" => "0.000",
        //       "quoteAssetVolume" => "0.00",
        //       "takerBuyAssetVolume" => "0.000",
        //       "takerBuyQuoteAssetVolume" => "0.00",
        //       "numberOfTrades" => 0,
        //       "start" => 1702453663894,
        //       "end" => 1702453663894,
        //       "isClosed" => true
        //     }
        // )
        //
        return $this->parse_ohlcvs($response, $market, $timeframe, $since, $limit);
    }

    public function parse_ohlcv($ohlcv, ?array $market = null): array {
        // example response in fetchOHLCV
        return array(
            $this->safe_integer($ohlcv, 'start'),
            $this->safe_number($ohlcv, 'open'),
            $this->safe_number($ohlcv, 'high'),
            $this->safe_number($ohlcv, 'low'),
            $this->safe_number($ohlcv, 'close'),
            $this->safe_number($ohlcv, 'volume'),
        );
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * get the list of most recent trades for a particular $symbol
         *
         * @see https://api-docs.defx.com/#5865452f-ea32-4f13-bfbc-03af5f5574fd
         *
         * @param {string} $symbol unified $symbol of the $market to fetch trades for
         * @param {int} [$since] timestamp in ms of the earliest trade to fetch
         * @param {int} [$limit] the maximum amount of trades to fetch
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $maxLimit = 50;
        if ($limit === null) {
            $limit = $maxLimit;
        }
        $limit = min ($maxLimit, $limit);
        $request = array(
            'symbol' => $market['id'],
            'limit' => $limit,
        );
        $response = $this->v1PublicGetSymbolsSymbolTrades ($this->extend($request, $params));
        //
        // array(
        //     {
        //       "buyerMaker" => "false",
        //       "price" => "2.0000",
        //       "qty" => "10.0000",
        //       "symbol" => "BTC_USDC",
        //       "timestamp" => "1702453663894"
        //     }
        // )
        //
        return $this->parse_trades($response, $market, $since, $limit);
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch all trades made by the user
         *
         * @see https://api-docs.defx.com/#06b5b33c-2fc6-48de-896c-fc316f5871a7
         *
         * @param {string} $symbol unified $symbol of the $market to fetch trades for
         * @param {int} [$since] timestamp in ms of the earliest trade to fetch
         * @param {int} [$limit] the maximum amount of trades to fetch
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~
         */
        $this->load_markets();
        $request = array();
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['symbols'] = $market['id'];
        }
        if ($limit !== null) {
            $maxLimit = 100;
            $limit = min ($maxLimit, $limit);
            $request['pageSize'] = $limit;
        }
        $response = $this->v1PrivateGetApiTrades ($this->extend($request, $params));
        //
        // {
        //     "data" => array(
        //         {
        //             "id" => "0192f665-c05b-7ba0-a080-8b6c99083489",
        //             "orderId" => "757730811259651728",
        //             "time" => "2024-11-04T08:58:36.474Z",
        //             "symbol" => "SOL_USDC",
        //             "side" => "SELL",
        //             "price" => "160.43600000",
        //             "qty" => "1.00",
        //             "fee" => "0.08823980",
        //             "role" => "TAKER",
        //             "pnl" => "0.00000000"
        //         }
        //     )
        // }
        //
        $data = $this->safe_list($response, 'data', array());
        return $this->parse_trades($data, null, $since, $limit);
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        //
        // fetchTrades
        //     {
        //       "buyerMaker" => "false",
        //       "price" => "2.0000",
        //       "qty" => "10.0000",
        //       "symbol" => "BTC_USDC",
        //       "timestamp" => "1702453663894"
        //     }
        //
        // fetchMyTrades
        //     {
        //         "id" => "0192f665-c05b-7ba0-a080-8b6c99083489",
        //         "orderId" => "757730811259651728",
        //         "time" => "2024-11-04T08:58:36.474Z",
        //         "symbol" => "SOL_USDC",
        //         "side" => "SELL",
        //         "price" => "160.43600000",
        //         "qty" => "1.00",
        //         "fee" => "0.08823980",
        //         "role" => "TAKER",
        //         "pnl" => "0.00000000"
        //     }
        //
        $time = $this->safe_string($trade, 'time');
        $timestamp = $this->safe_integer($trade, 'timestamp', $this->parse8601($time));
        $marketId = $this->safe_string($trade, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        $price = $this->safe_string($trade, 'price');
        $amount = $this->safe_string($trade, 'qty');
        $id = $this->safe_string($trade, 'id');
        $oid = $this->safe_string($trade, 'orderId');
        $takerOrMaker = $this->safe_string_lower($trade, 'role');
        $buyerMaker = $this->safe_bool($trade, 'buyerMaker');
        $side = $this->safe_string_lower($trade, 'side');
        if ($buyerMaker !== null) {
            if ($buyerMaker) {
                $side = 'sell';
            } else {
                $side = 'buy';
            }
        }
        return $this->safe_trade(array(
            'id' => $id,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'symbol' => $symbol,
            'side' => $side,
            'price' => $price,
            'amount' => $amount,
            'cost' => null,
            'order' => $oid,
            'takerOrMaker' => $takerOrMaker,
            'type' => null,
            'fee' => array(
                'cost' => $this->safe_string($trade, 'fee'),
                'currency' => 'USDC',
            ),
            'info' => $trade,
        ), $market);
    }

    public function fetch_order_book(string $symbol, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
         *
         * @see https://api-docs.defx.com/#6c1a2971-8325-4e7d-9962-e0bfcaacf9c4
         *
         * @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
         * @param {string} [$params->slab] $slab from $market->info.depthSlabs
         * @return {array} A dictionary of ~@link https://docs.ccxt.com/#/?id=order-book-structure order book structures~ indexed by $market symbols
         */
        $this->load_markets();
        $market = $this->market($symbol);
        if ($limit === null) {
            $limit = 10; // $limit must be one of [5, 10, 20]
        }
        $marketInfo = $this->safe_dict($market, 'info', array());
        $slab = $this->safe_list($marketInfo, 'depthSlabs', array());
        $request = array(
            'symbol' => $market['id'],
            'level' => $limit,
            'slab' => $this->safe_string($slab, 0),
        );
        $response = $this->v1PublicGetSymbolsSymbolDepthLevelSlab ($this->extend($request, $params));
        //
        // {
        //     "symbol" => "ETH_USDC",
        //     "level" => "5",
        //     "slab" => "1",
        //     "lastTradeTimestamp" => "1708313446812",
        //     "timestamp" => "1708313446812",
        //     "bids" => array(
        //       {
        //         "price" => "1646.16",
        //         "qty" => "0.001"
        //       }
        //     ),
        //     "asks" => array(
        //       {
        //         "price" => "1646.16",
        //         "qty" => "0.001"
        //       }
        //     )
        // }
        //
        $timestamp = $this->safe_integer($response, 'timestamp');
        return $this->parse_order_book($response, $symbol, $timestamp, 'bids', 'asks', 'price', 'qty');
    }

    public function fetch_mark_price(string $symbol, $params = array ()): array {
        /**
         * fetches mark price for the $market
         *
         * @see https://api-docs.defx.com/#12168192-4e7b-4458-a001-e8b80961f0b7
         *
         * @param {string} $symbol unified $symbol of the $market to fetch the ticker for
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {string} [$params->subType] "linear" or "inverse"
         * @return {array} a dictionary of ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structures~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = $this->v1PublicGetSymbolsSymbolPrices ($this->extend($request, $params));
        //
        // {
        //     "markPrice" => "100.00",
        //     "indexPrice" => "100.00",
        //     "ltp" => "101.34",
        //     "movingFundingRate" => "0.08",
        //     "payoutFundingRate" => "-0.03",
        //     "nextFundingPayout" => 1711555532146
        // }
        //
        return $this->parse_ticker($response, $market);
    }

    public function fetch_funding_rate(string $symbol, $params = array ()): array {
        /**
         * fetch the current funding rate
         *
         * @see https://api-docs.defx.com/#12168192-4e7b-4458-a001-e8b80961f0b7
         *
         * @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~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = $this->v1PublicGetSymbolsSymbolPrices ($this->extend($request, $params));
        //
        // {
        //     "markPrice" => "100.00",
        //     "indexPrice" => "100.00",
        //     "ltp" => "101.34",
        //     "movingFundingRate" => "0.08",
        //     "payoutFundingRate" => "-0.03",
        //     "nextFundingPayout" => 1711555532146
        // }
        //
        return $this->parse_funding_rate($response, $market);
    }

    public function parse_funding_rate($contract, ?array $market = null): array {
        //
        // {
        //     "markPrice" => "100.00",
        //     "indexPrice" => "100.00",
        //     "ltp" => "101.34",
        //     "movingFundingRate" => "0.08",
        //     "payoutFundingRate" => "-0.03",
        //     "nextFundingPayout" => 1711555532146
        // }
        //
        $markPrice = $this->safe_number($contract, 'markPrice');
        $indexPrice = $this->safe_number($contract, 'indexPrice');
        $fundingRate = $this->safe_number($contract, 'payoutFundingRate');
        $fundingTime = $this->safe_integer($contract, 'nextFundingPayout');
        return array(
            'info' => $contract,
            'symbol' => $market['symbol'],
            'markPrice' => $markPrice,
            'indexPrice' => $indexPrice,
            'interestRate' => null,
            'estimatedSettlePrice' => null,
            'timestamp' => null,
            'datetime' => null,
            'fundingRate' => $fundingRate,
            'fundingTimestamp' => $fundingTime,
            'fundingDatetime' => $this->iso8601($fundingTime),
            'nextFundingRate' => null,
            'nextFundingTimestamp' => null,
            'nextFundingDatetime' => null,
            'previousFundingRate' => null,
            'previousFundingTimestamp' => null,
            'previousFundingDatetime' => null,
            'interval' => null,
        );
    }

    public function fetch_balance($params = array ()): array {
        /**
         * query for balance and get the amount of funds available for trading or funds locked in orders
         *
         * @see https://api-docs.defx.com/#26414338-14f7-40a1-b246-f8ea8571493f
         *
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
         */
        $this->load_markets();
        $response = $this->v1PrivateGetApiWalletBalance ($params);
        //
        // {
        //     "assets" => array(
        //       {
        //         "asset" => "USDC",
        //         "balance" => "0.000"
        //       }
        //     )
        // }
        //
        $data = $this->safe_list($response, 'assets');
        return $this->parse_balance($data);
    }

    public function parse_balance($balances): array {
        $result = array(
            'info' => $balances,
        );
        for ($i = 0; $i < count($balances); $i++) {
            $balance = $balances[$i];
            $code = $this->safe_currency_code($this->safe_string($balance, 'asset'));
            $account = $this->account();
            $account['total'] = $this->safe_string($balance, 'balance');
            $result[$code] = $account;
        }
        return $this->safe_balance($result);
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        /**
         * create a trade order
         *
         * @see https://api-docs.defx.com/#ba222d88-8856-4d3c-87a9-7cec07bb2622
         *
         * @param {string} $symbol unified $symbol of the $market to create an order in
         * @param {string} $type 'market' or 'limit'
         * @param {string} $side 'buy' or 'sell'
         * @param {float} $amount how much of currency you want to trade in units of base currency
         * @param {float} [$price] the $price at which the order is to be fulfilled, in units of the quote currency, ignored in $market orders
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {float} [$params->triggerPrice] The $price a trigger order is triggered at
         * @param {string} [$params->reduceOnly] for swap and future $reduceOnly is a string 'true' or 'false' that cant be sent with close position set to true or in hedge mode. For spot margin and option $reduceOnly is a boolean.
         * @return {array} an ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $reduceOnly = $this->safe_bool_2($params, 'reduceOnly', 'reduce_only');
        $params = $this->omit($params, array( 'reduceOnly', 'reduce_only' ));
        $orderType = strtoupper($type);
        $orderSide = strtoupper($side);
        $request = array(
            'symbol' => $market['id'],
            'side' => $orderSide,
            'type' => $orderType,
        );
        $takeProfitPrice = $this->safe_string($params, 'takeProfitPrice');
        $triggerPrice = $this->safe_string_2($params, 'stopPrice', 'triggerPrice');
        $isMarket = $orderType === 'MARKET';
        $isLimit = $orderType === 'LIMIT';
        $timeInForce = $this->safe_string_upper($params, 'timeInForce');
        if ($timeInForce !== null) {
            // GTC, IOC, FOK, AON
            $request['timeInForce'] = $timeInForce;
        } else {
            if ($isLimit) {
                $request['timeInForce'] = 'GTC';
            }
        }
        if ($reduceOnly) {
            $request['reduceOnly'] = $reduceOnly;
        }
        $clientOrderId = $this->safe_string($params, 'clientOrderId');
        if ($clientOrderId !== null) {
            $request['newClientOrderId'] = $clientOrderId;
        }
        if ($triggerPrice !== null || $takeProfitPrice !== null) {
            $request['workingType'] = 'MARK_PRICE';
            if ($takeProfitPrice !== null) {
                $request['stopPrice'] = $this->price_to_precision($symbol, $takeProfitPrice);
                if ($isMarket) {
                    $request['type'] = 'TAKE_PROFIT_MARKET';
                } else {
                    $request['type'] = 'TAKE_PROFIT_LIMIT';
                }
            } else {
                $request['stopPrice'] = $this->price_to_precision($symbol, $triggerPrice);
                if ($isMarket) {
                    $request['type'] = 'STOP_MARKET';
                } else {
                    $request['type'] = 'STOP_LIMIT';
                }
            }
        }
        if ($isLimit && $price !== null) {
            $request['price'] = $this->price_to_precision($symbol, $price);
        }
        $request['quantity'] = $this->amount_to_precision($symbol, $amount);
        $params = $this->omit($params, array( 'clOrdID', 'clientOrderId', 'client_order_id', 'postOnly', 'timeInForce', 'stopPrice', 'triggerPrice', 'takeProfitPrice' ));
        $response = $this->v1PrivatePostApiOrder ($this->extend($request, $params));
        //
        // {
        //     "success" => true,
        //     "data" => {
        //       "orderId" => "",
        //       "clientOrderId" => "",
        //       "cumulativeQty" => "",
        //       "cumulativeQuote" => "",
        //       "executedQty" => "",
        //       "avgPrice" => "",
        //       "origQty" => "",
        //       "price" => "",
        //       "reduceOnly" => true,
        //       "side" => "",
        //       "status" => "",
        //       "symbol" => "",
        //       "timeInForce" => "",
        //       "type" => "",
        //       "workingType" => ""
        //     }
        // }
        //
        $data = $this->safe_dict($response, 'data');
        return $this->parse_order($data, $market);
    }

    public function parse_order_status(?string $status) {
        if ($status !== null) {
            $statuses = array(
                'NEW' => 'open',
                'OPEN' => 'open',
                'CANCELLED' => 'canceled',
                'REJECTED' => 'rejected',
                'FILLED' => 'closed',
            );
            return $this->safe_string($statuses, $status, $status);
        }
        return $status;
    }

    public function parse_order(array $order, ?array $market = null): array {
        //
        // {
        //     "orderId" => "746472647227344528",
        //     "createdAt" => "2024-10-25T16:49:31.077Z",
        //     "updatedAt" => "2024-10-25T16:49:31.378Z",
        //     "clientOrderId" => "0192c495-49c3-71ee-b3d3-7442a2090807",
        //     "reduceOnly" => false,
        //     "side" => "SELL",
        //     "status" => "FILLED",
        //     "symbol" => "SOL_USDC",
        //     "timeInForce" => "GTC",
        //     "type" => "MARKET",
        //     "origQty" => "0.80",
        //     "executedQty" => "0.80",
        //     "cumulativeQuote" => "137.87440000",
        //     "avgPrice" => "172.34300000",
        //     "totalPnL" => "0.00000000",
        //     "totalFee" => "0.07583092",
        //     "workingType" => null,
        //     "postOnly" => false,
        //     "linkedOrderParentType" => null,
        //     "isTriggered" => false,
        //     "slippagePercentage" => "5"
        // }
        //
        $orderId = $this->safe_string($order, 'orderId');
        $clientOrderId = $this->safe_string($order, 'clientOrderId');
        $marketId = $this->safe_string($order, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $symbol = $market['symbol'];
        $price = $this->safe_string($order, 'price');
        $amount = $this->safe_string($order, 'origQty');
        $orderType = $this->safe_string_lower($order, 'type');
        $status = $this->safe_string($order, 'status');
        $side = $this->safe_string_lower($order, 'side');
        $filled = $this->omit_zero($this->safe_string($order, 'executedQty'));
        $average = $this->omit_zero($this->safe_string($order, 'avgPrice'));
        $timeInForce = $this->safe_string_lower($order, 'timeInForce');
        $takeProfitPrice = null;
        $triggerPrice = null;
        if ($orderType !== null) {
            if (mb_strpos($orderType, 'take_profit') !== false) {
                $takeProfitPrice = $this->safe_string($order, 'stopPrice');
            } else {
                $triggerPrice = $this->safe_string($order, 'stopPrice');
            }
        }
        $timestamp = $this->parse8601($this->safe_string($order, 'createdAt'));
        $lastTradeTimestamp = $this->parse8601($this->safe_string($order, 'updatedAt'));
        return $this->safe_order(array(
            'id' => $orderId,
            'clientOrderId' => $clientOrderId,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => $lastTradeTimestamp,
            'lastUpdateTimestamp' => $lastTradeTimestamp,
            'status' => $this->parse_order_status($status),
            'symbol' => $symbol,
            'type' => $orderType,
            'timeInForce' => $timeInForce,
            'postOnly' => $this->safe_bool($order, 'postOnly'),
            'reduceOnly' => $this->safe_bool($order, 'reduceOnly'),
            'side' => $side,
            'price' => $price,
            'triggerPrice' => $triggerPrice,
            'takeProfitPrice' => $takeProfitPrice,
            'stopLossPrice' => null,
            'average' => $average,
            'amount' => $amount,
            'filled' => $filled,
            'remaining' => null,
            'cost' => null,
            'trades' => null,
            'fee' => array(
                'cost' => $this->safe_string($order, 'totalFee'),
                'currency' => 'USDC',
            ),
            'info' => $order,
        ), $market);
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        /**
         *
         * @see https://api-docs.defx.com/#09186f23-f8d1-4993-acf4-9974d8a6ddb0
         *
         * cancels an open order
         * @param {string} $id order $id
         * @param {string} $symbol unified $symbol of the $market the order was made in
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
         */
        $this->load_markets();
        $request = array(
            'orderId' => $id,
            'idType' => 'orderId',
        );
        $clientOrderId = $this->safe_string_n($params, array( 'clOrdID', 'clientOrderId', 'client_order_id' ));
        $isByClientOrder = $clientOrderId !== null;
        if ($isByClientOrder) {
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' cancelOrder() requires a $symbol argument');
            }
            $market = $this->market($symbol);
            $request['orderId'] = $clientOrderId;
            $request['idType'] = 'clientOrderId';
            $request['symbol'] = $market['id'];
        }
        $params = $this->omit($params, array( 'clOrdID', 'clientOrderId', 'client_order_id' ));
        $response = $this->v1PrivateDeleteApiOrderOrderId ($this->extend($request, $params));
        //
        // {
        //     "success" => true
        // }
        //
        $extendParams = array( 'symbol' => $symbol );
        if ($isByClientOrder) {
            $extendParams['clientOrderId'] = $clientOrderId;
        } else {
            $extendParams['id'] = $id;
        }
        return $this->extend($this->parse_order($response), $extendParams);
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        /**
         * cancel all open orders
         *
         * @see https://api-docs.defx.com/#db5531da-3692-4a53-841f-6ad6495f823a
         *
         * @param {string} $symbol unified $market $symbol, only orders in the $market of this $symbol are cancelled when $symbol is not null
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbols' => [ $market['id'] ],
        );
        $response = $this->v1PrivateDeleteApiOrdersAllOpen ($this->extend($request, $params));
        //
        // {
        //     "data" => {
        //         "msg" => "The operation of cancel all open order is done."
        //     }
        // }
        //
        return array( $this->safe_order(array( 'info' => $response )) );
    }

    public function fetch_position(string $symbol, $params = array ()) {
        /**
         * fetch $data on a single open contract trade position
         *
         * @see https://api-docs.defx.com/#d89dbb86-9aba-4f59-ac5d-a97ff25ea80e
         *
         * @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~
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' fetchPosition() requires a $symbol argument');
        }
        $this->load_markets();
        $market = $this->market($symbol);
        $request = array(
            'symbol' => $market['id'],
        );
        $response = $this->v1PrivateGetApiPositionActive ($this->extend($request, $params));
        //
        // {
        //     "data" => array(
        //         {
        //             "positionId" => "0192c495-4a68-70ee-9081-9d368bd16dfc",
        //             "symbol" => "SOL_USDC",
        //             "positionSide" => "SHORT",
        //             "entryPrice" => "172.34300000",
        //             "quantity" => "0.80",
        //             "marginAmount" => "20.11561173",
        //             "marginAsset" => "USDC",
        //             "pnl" => "0.00000000"
        //         }
        //     )
        // }
        //
        $data = $this->safe_list($response, 'data', array());
        $first = $this->safe_dict($data, 0, array());
        return $this->parse_position($first, $market);
    }

    public function fetch_positions(?array $symbols = null, $params = array ()): array {
        /**
         * fetch all open $positions
         *
         * @see https://api-docs.defx.com/#d89dbb86-9aba-4f59-ac5d-a97ff25ea80e
         *
         * @param {string[]} [$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~
         */
        $this->load_markets();
        $response = $this->v1PrivateGetApiPositionActive ($params);
        //
        // {
        //     "data" => array(
        //         {
        //             "positionId" => "0192c495-4a68-70ee-9081-9d368bd16dfc",
        //             "symbol" => "SOL_USDC",
        //             "positionSide" => "SHORT",
        //             "entryPrice" => "172.34300000",
        //             "quantity" => "0.80",
        //             "marginAmount" => "20.11561173",
        //             "marginAsset" => "USDC",
        //             "pnl" => "0.00000000"
        //         }
        //     )
        // }
        //
        $positions = $this->safe_list($response, 'data', array());
        return $this->parse_positions($positions, $symbols);
    }

    public function parse_position(array $position, ?array $market = null) {
        //
        // {
        //     "positionId" => "0192c495-4a68-70ee-9081-9d368bd16dfc",
        //     "symbol" => "SOL_USDC",
        //     "positionSide" => "SHORT",
        //     "entryPrice" => "172.34300000",
        //     "quantity" => "0.80",
        //     "marginAmount" => "20.11561173",
        //     "marginAsset" => "USDC",
        //     "pnl" => "0.00000000"
        // }
        //
        $marketId = $this->safe_string($position, 'symbol');
        $market = $this->safe_market($marketId, $market);
        $size = Precise::string_abs($this->safe_string($position, 'quantity'));
        $side = $this->safe_string_lower($position, 'positionSide');
        $unrealisedPnl = $this->omit_zero($this->safe_string($position, 'pnl'));
        $entryPrice = $this->omit_zero($this->safe_string($position, 'entryPrice'));
        $initialMargin = $this->safe_string($position, 'marginAmount');
        return $this->safe_position(array(
            'info' => $position,
            'id' => $this->safe_string($position, 'positionId'),
            'symbol' => $market['symbol'],
            'timestamp' => null,
            'datetime' => null,
            'lastUpdateTimestamp' => null,
            'initialMargin' => $this->parse_number($initialMargin),
            'initialMarginPercentage' => null,
            'maintenanceMargin' => null,
            'maintenanceMarginPercentage' => null,
            'entryPrice' => $this->parse_number($entryPrice),
            'notional' => null,
            'leverage' => null,
            'unrealizedPnl' => $this->parse_number($unrealisedPnl),
            'realizedPnl' => null,
            'contracts' => $this->parse_number($size),
            'contractSize' => $this->safe_number($market, 'contractSize'),
            'marginRatio' => null,
            'liquidationPrice' => null,
            'markPrice' => null,
            'lastPrice' => null,
            'collateral' => null,
            'marginMode' => null,
            'side' => $side,
            'percentage' => null,
            'stopLossPrice' => null,
            'takeProfitPrice' => null,
            'hedged' => null,
        ));
    }

    public function fetch_order(string $id, ?string $symbol = null, $params = array ()) {
        /**
         * fetches information on an order made by the user
         *
         * @see https://api-docs.defx.com/#44f82dd5-26b3-4e1f-b4aa-88ceddd65237
         *
         * @param {string} $id the order $id
         * @param {string} $symbol unified $symbol of the $market the order was made in
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} An ~@link https://docs.ccxt.com/#/?$id=order-structure order structure~
         */
        $this->load_markets();
        $request = array(
            'orderId' => $id,
            'idType' => 'orderId',
        );
        $clientOrderId = $this->safe_string_n($params, array( 'clOrdID', 'clientOrderId', 'client_order_id' ));
        $params = $this->omit($params, array( 'clOrdID', 'clientOrderId', 'client_order_id' ));
        if ($clientOrderId !== null) {
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' fetchOrder() requires a $symbol argument');
            }
            $market = $this->market($symbol);
            $request['orderId'] = $clientOrderId;
            $request['idType'] = 'clientOrderId';
            $request['symbol'] = $market['id'];
        }
        $response = $this->v1PrivateGetApiOrderOrderId ($this->extend($request, $params));
        //
        // {
        //     "success" => true,
        //     "data" => {
        //         "orderId" => "555068654076559792",
        //         "createdAt" => "2024-05-08T05:45:42.148Z",
        //         "updatedAt" => "2024-05-08T05:45:42.166Z",
        //         "clientOrderId" => "dummyClientOrderId",
        //         "reduceOnly" => false,
        //         "side" => "SELL",
        //         "status" => "REJECTED",
        //         "symbol" => "BTC_USDC",
        //         "timeInForce" => "GTC",
        //         "type" => "TAKE_PROFIT_MARKET",
        //         "origQty" => "1.000",
        //         "executedQty" => "0.000",
        //         "cumulativeQuote" => "0.00",
        //         "avgPrice" => "0.00",
        //         "stopPrice" => "65000.00",
        //         "totalPnL" => "0.00",
        //         "workingType" => "MARK_PRICE",
        //         "postOnly" => false
        //     }
        // }
        //
        $data = $this->safe_dict($response, 'data');
        return $this->parse_order($data);
    }

    public function fetch_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on multiple orders made by the user
         *
         * @see https://api-docs.defx.com/#ab200038-8acb-4170-b05e-4fcb4cc13751
         *
         * @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 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
         * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $this->load_markets();
        $request = array();
        if ($symbol !== null) {
            $market = $this->market($symbol);
            $request['symbols'] = $market['id'];
        }
        $until = $this->safe_integer($params, 'until');
        if ($until !== null) {
            $params = $this->omit($params, 'until');
            $request['end'] = $this->iso8601($until);
        }
        if ($since !== null) {
            $request['start'] = $this->iso8601($since);
        }
        if ($limit !== null) {
            $maxLimit = 100;
            $limit = min ($maxLimit, $limit);
            $request['pageSize'] = $limit;
        }
        $response = $this->v1PrivateGetApiOrders ($this->extend($request, $params));
        //
        // {
        //     "data" => array(
        //         {
        //             "orderId" => "746472647227344528",
        //             "createdAt" => "2024-10-25T16:49:31.077Z",
        //             "updatedAt" => "2024-10-25T16:49:31.378Z",
        //             "clientOrderId" => "0192c495-49c3-71ee-b3d3-7442a2090807",
        //             "reduceOnly" => false,
        //             "side" => "SELL",
        //             "status" => "FILLED",
        //             "symbol" => "SOL_USDC",
        //             "timeInForce" => "GTC",
        //             "type" => "MARKET",
        //             "origQty" => "0.80",
        //             "executedQty" => "0.80",
        //             "cumulativeQuote" => "137.87440000",
        //             "avgPrice" => "172.34300000",
        //             "totalPnL" => "0.00000000",
        //             "totalFee" => "0.07583092",
        //             "workingType" => null,
        //             "postOnly" => false,
        //             "linkedOrderParentType" => null,
        //             "isTriggered" => false,
        //             "slippagePercentage" => 5
        //         }
        //     )
        // }
        //
        $data = $this->safe_list($response, 'data', array());
        return $this->parse_orders($data, null, $since, $limit);
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch all unfilled currently open orders
         *
         * @see https://api-docs.defx.com/#ab200038-8acb-4170-b05e-4fcb4cc13751
         *
         * @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 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
         * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $req = array(
            'statuses' => 'OPEN',
        );
        return $this->fetch_orders($symbol, $since, $limit, $this->extend($req, $params));
    }

    public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on multiple closed orders made by the user
         *
         * @see https://api-docs.defx.com/#ab200038-8acb-4170-b05e-4fcb4cc13751
         *
         * @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 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
         * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $req = array(
            'statuses' => 'FILLED',
        );
        return $this->fetch_orders($symbol, $since, $limit, $this->extend($req, $params));
    }

    public function fetch_canceled_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetches information on multiple canceled orders made by the user
         *
         * @see https://api-docs.defx.com/#ab200038-8acb-4170-b05e-4fcb4cc13751
         *
         * @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 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
         * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
         */
        $req = array(
            'statuses' => 'CANCELED',
        );
        return $this->fetch_orders($symbol, $since, $limit, $this->extend($req, $params));
    }

    public function close_position(string $symbol, ?string $side = null, $params = array ()): array {
        /**
         * closes an open position for a market
         *
         * @see https://api-docs.defx.com/#b2c08074-c4d9-4e50-b637-0d6c498fa29e
         *
         * @param {string} $symbol unified CCXT market $symbol
         * @param {string} [$side] one-way mode => 'buy' or 'sell', hedge-mode => 'long' or 'short'
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {string} [$params->positionId] the position id you want to close
         * @param {string} [$params->type] 'MARKET' or 'LIMIT'
         * @param {string} [$params->quantity] how much of currency you want to trade in units of base currency
         * @param {string} [$params->price] the $price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
         * @return {array} An ~@link https://docs.ccxt.com/#/?id=order-structure order structure~
         */
        $this->load_markets();
        $positionId = $this->safe_string($params, 'positionId');
        if ($positionId === null) {
            throw new ArgumentsRequired($this->id . ' closePosition() requires a positionId');
        }
        $type = $this->safe_string_upper($params, 'type');
        if ($type === null) {
            throw new ArgumentsRequired($this->id . ' closePosition() requires a type');
        }
        $quantity = $this->safe_string($params, 'quantity');
        if ($quantity === null) {
            throw new ArgumentsRequired($this->id . ' closePosition() requires a quantity');
        }
        $request = array(
            'positionId' => $positionId,
            'type' => $type,
            'quantity' => $quantity,
        );
        if ($type !== 'MARKET') {
            $price = $this->safe_string($params, 'price');
            if ($price === null) {
                throw new ArgumentsRequired($this->id . ' closePosition() requires a price');
            }
            $request['price'] = $price;
        }
        $params = $this->omit($params, array( 'positionId', 'type', 'quantity', 'price' ));
        $response = $this->v1PrivateDeleteApiPositionPositionId ($this->extend($request, $params));
        //
        //     array()
        //
        return $response;
    }

    public function close_all_positions($params = array ()): array {
        /**
         * closes all open positions for a market type
         *
         * @see https://api-docs.defx.com/#d6f63b43-100e-47a9-998c-8b6c0c72d204
         *
         * @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 structures~
         */
        $this->load_markets();
        $response = $this->v1PrivateDeleteApiPositionAll ($params);
        //
        // {
        //     "data" => array(
        //         {
        //             "positionId" => "d6ca1a27-28ad-47ae-b244-0bda5ac37b2b",
        //             "success" => true
        //         }
        //     )
        // }
        //
        $data = $this->safe_list($response, 'data', array());
        return $this->parse_positions($data, null, $params);
    }

    public function fetch_ledger(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): array {
        /**
         * fetch the history of changes, actions done by the user or operations that altered the balance of the user
         *
         * @see https://api-docs.defx.com/#38cc8974-794f-48c0-b959-db045a0ee565
         *
         * @param {string} [$code] unified currency $code
         * @param {int} [$since] timestamp in ms of the earliest ledger entry
         * @param {int} [$limit] max number of ledger entries to return
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @param {int} [$params->until] timestamp in ms of the latest ledger entry
         * @param {boolean} [$params->paginate] default false, when true will automatically $paginate by calling this endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-$params)
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=ledger ledger structure~
         */
        $this->load_markets();
        $paginate = false;
        list($paginate, $params) = $this->handle_option_and_params($params, 'fetchLedger', 'paginate');
        if ($paginate) {
            return $this->fetch_paginated_call_dynamic('fetchLedger', $code, $since, $limit, $params);
        }
        $request = array();
        if ($since !== null) {
            $request['start'] = $since;
        } else {
            $request['start'] = 0;
        }
        $until = $this->safe_integer($params, 'until');
        if ($until !== null) {
            $params = $this->omit($params, 'until');
            $request['end'] = $until;
        } else {
            $request['end'] = $this->milliseconds();
        }
        $response = $this->v1PrivateGetApiWalletTransactions ($this->extend($request, $params));
        $data = $this->safe_list($response, 'transactions', array());
        return $this->parse_ledger($data, null, $since, $limit);
    }

    public function parse_ledger_entry(array $item, ?array $currency = null): array {
        //
        // {
        //     "id" => "01JCSZS6H5VQND3GF5P98SJ29C",
        //     "timestamp" => 1731744012054,
        //     "type" => "FundingFee",
        //     "amount" => "0.02189287",
        //     "asset" => "USDC",
        //     "operation" => "CREDIT"
        // }
        //
        $amount = $this->safe_string($item, 'amount');
        $currencyId = $this->safe_string($item, 'asset');
        $code = $this->safe_currency_code($currencyId, $currency);
        $currency = $this->safe_currency($currencyId, $currency);
        $timestamp = $this->safe_integer($item, 'timestamp');
        $type = $this->safe_string($item, 'type');
        return $this->safe_ledger_entry(array(
            'info' => $item,
            'id' => $this->safe_string($item, 'id'),
            'direction' => null,
            'account' => null,
            'referenceAccount' => null,
            'referenceId' => null,
            'type' => $this->parse_ledger_entry_type($type),
            'currency' => $code,
            'amount' => $this->parse_number($amount),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'before' => null,
            'after' => null,
            'status' => null,
            'fee' => null,
        ), $currency);
    }

    public function parse_ledger_entry_type($type) {
        $ledgerType = array(
            'FundingFee' => 'fee',
            'FeeRebate' => 'fee',
            'FeeKickback' => 'fee',
            'RealizedPnl' => 'trade',
            'LiquidationClearance' => 'trade',
            'Transfer' => 'transfer',
            'ReferralPayout' => 'referral',
            'Commission' => 'commission',
        );
        return $this->safe_string($ledgerType, $type, $type);
    }

    public function withdraw(string $code, float $amount, string $address, ?string $tag = null, $params = array ()): array {
        /**
         * make a withdrawal
         *
         * @see https://api-docs.defx.com/#2600f503-63ed-4672-b8f6-69ea5f03203b
         *
         * @param {string} $code unified $currency $code
         * @param {float} $amount the $amount to withdraw
         * @param {string} $address the $address to withdraw to
         * @param {string} $tag
         * @param {array} [$params] extra parameters specific to the exchange API endpoint
         * @return {array} a ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structure~
         */
        $this->load_markets();
        $currency = $this->currency($code);
        $request = array(
            'amount' => $this->currency_to_precision($code, $amount),
            'asset' => $currency['id'],
            // 'network' => 'ARB_SEPOLIA',
            // 'chainId' => '421614',
        );
        $response = $this->v1PrivatePostApiTransfersBridgeWithdrawal ($this->extend($request, $params));
        //
        // {
        //     "transactionId" => "0x301e5851e5aefa733abfbc8b30817ca3b61601e0ddf1df8c59656fb888b0bc9c"
        // }
        //
        return $this->parse_transaction($response, $currency);
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // withdraw
        //
        // {
        //     "transactionId" => "0x301e5851e5aefa733abfbc8b30817ca3b61601e0ddf1df8c59656fb888b0bc9c"
        // }
        //
        $txid = $this->safe_string($transaction, 'transactionId');
        return array(
            'info' => $transaction,
            'id' => null,
            'txid' => $txid,
            'timestamp' => null,
            'datetime' => null,
            'network' => null,
            'address' => null,
            'addressTo' => null,
            'addressFrom' => null,
            'tag' => null,
            'tagTo' => null,
            'tagFrom' => null,
            'type' => null,
            'amount' => null,
            'currency' => $this->safe_currency_code(null, $currency),
            'status' => null,
            'updated' => null,
            'internal' => null,
            'comment' => null,
            'fee' => null,
        );
    }

    public function set_leverage(int $leverage, ?string $symbol = null, $params = array ()) {
        /**
         * set the level of $leverage for a $market
         *
         * @see https://api-docs.defx.com/#4cb4ecc4-6c61-4194-8353-be67faaf7ca7
         *
         * @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
         */
        if ($symbol === null) {
            throw new ArgumentsRequired($this->id . ' setLeverage() requires a $symbol argument');
        }
        $this->load_markets();
        $request = array(
            'leverage' => $this->number_to_string($leverage),
        );
        $market = $this->market($symbol);
        $request['symbol'] = $market['id'];
        $response = $this->v1PrivatePostApiUsersMetadataLeverage ($this->extend($request, $params));
        //
        // {
        //     "success" => true,
        //     "data" => {
        //       "leverage" => "11",
        //       "symbol" => "BTC_USDC"
        //     }
        // }
        //
        $data = $this->safe_dict($response, 'data', array());
        return $this->parse_leverage($data, $market);
    }

    public function parse_leverage(array $leverage, ?array $market = null): array {
        //
        //     "data" => {
        //       "leverage" => "11",
        //       "symbol" => "BTC_USDC"
        //     }
        //
        $marketId = $this->safe_string($leverage, 'symbol');
        $leverageValue = $this->safe_integer($leverage, 'leverage');
        return array(
            'info' => $leverage,
            'symbol' => $this->safe_symbol($marketId, $market),
            'marginMode' => null,
            'longLeverage' => $leverageValue,
            'shortLeverage' => $leverageValue,
        );
    }

    public function nonce() {
        return $this->milliseconds();
    }

    public function sign($path, $section = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $version = $section[0];
        $access = $section[1];
        $pathWithParams = $this->implode_params($path, $params);
        $url = $this->implode_hostname($this->urls['api'][$access]);
        $url .= '/' . $version . '/';
        $params = $this->omit($params, $this->extract_params($path));
        $params = $this->keysort($params);
        if ($access === 'public') {
            $url .= 'open/' . $pathWithParams;
            if ($params) {
                $url .= '?' . $this->rawencode($params);
            }
        } else {
            $this->check_required_credentials();
            $headers = array( 'X-DEFX-SOURCE' => 'ccxt' );
            $url .= 'auth/' . $pathWithParams;
            $nonce = (string) $this->milliseconds();
            $payload = $nonce;
            if ($method === 'GET' || $path === 'api/order/{orderId}') {
                $payload .= $this->rawencode($params);
                if ($params) {
                    $url .= '?' . $this->rawencode($params);
                }
            } else {
                if ($params !== null) {
                    $body = $this->json($params);
                    $payload .= $body;
                }
                $headers['Content-Type'] = 'application/json';
            }
            $signature = $this->hmac($this->encode($payload), $this->encode($this->secret), 'sha256');
            $headers['X-DEFX-APIKEY'] = $this->apiKey;
            $headers['X-DEFX-TIMESTAMP'] = $nonce;
            $headers['X-DEFX-SIGNATURE'] = $signature;
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }

    public function handle_errors(int $httpCode, string $reason, string $url, string $method, array $headers, string $body, $response, $requestHeaders, $requestBody) {
        if (!$response) {
            return null; // fallback to default error handler
        }
        // array("errorCode":404,"errorMessage":"Not Found")
        // array("msg":"Missing auth signature","code":"missing_auth_signature")
        // array("success":false,"err":array("msg":"Invalid order id","code":"invalid_order_id"))
        $success = $this->safe_bool($response, 'success');
        $err = $this->safe_dict($response, 'err', $response);
        $errorCode = $this->safe_string_2($err, 'errorCode', 'code');
        if (!$success) {
            $feedback = $this->id . ' ' . $this->json($response);
            $this->throw_broadly_matched_exception($this->exceptions['broad'], $body, $feedback);
            $this->throw_exactly_matched_exception($this->exceptions['exact'], $errorCode, $feedback);
        }
        return null;
    }

    public function default_network_code_for_currency($code) {
        $currencyItem = $this->currency($code);
        $networks = $currencyItem['networks'];
        $networkKeys = is_array($networks) ? array_keys($networks) : array();
        for ($i = 0; $i < count($networkKeys); $i++) {
            $network = $networkKeys[$i];
            if ($network === 'ETH') {
                return $network;
            }
        }
        // if it was not returned according to above options, then return the first $network of currency
        return $this->safe_value($networkKeys, 0);
    }

    public function set_sandbox_mode(bool $enable) {
        parent::set_sandbox_mode($enable);
        $this->options['sandboxMode'] = $enable;
    }
}
