<?php

namespace ccxt\async;

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

use Exception; // a common import
use ccxt\async\abstract\derive as Exchange;
use ccxt\ExchangeError;
use ccxt\ArgumentsRequired;
use ccxt\InvalidOrder;
use ccxt\Precise;
use \React\Async;
use \React\Promise;
use \React\Promise\PromiseInterface;

class derive extends Exchange {

    public function describe(): mixed {
        return $this->deep_extend(parent::describe(), array(
            'id' => 'derive',
            'name' => 'derive',
            'countries' => array(),
            'version' => 'v1',
            'rateLimit' => 50,
            'certified' => false,
            'pro' => true,
            'dex' => true,
            'has' => array(
                'CORS' => null,
                'spot' => false,
                'margin' => false,
                'swap' => false,
                'future' => false,
                'option' => false,
                'addMargin' => false,
                'borrowCrossMargin' => false,
                'borrowIsolatedMargin' => false,
                'cancelAllOrders' => true,
                'cancelAllOrdersAfter' => false,
                'cancelOrder' => true,
                'cancelOrders' => false,
                'cancelOrdersForSymbols' => false,
                'closeAllPositions' => false,
                'closePosition' => false,
                'createMarketBuyOrderWithCost' => false,
                'createMarketOrderWithCost' => false,
                'createMarketSellOrderWithCost' => false,
                'createOrder' => true,
                'createOrders' => false,
                'createReduceOnlyOrder' => false,
                'createStopOrder' => false,
                'createTriggerOrder' => false,
                'editOrder' => true,
                'fetchAccounts' => false,
                'fetchBalance' => true,
                'fetchBorrowInterest' => false,
                'fetchBorrowRateHistories' => false,
                'fetchBorrowRateHistory' => false,
                'fetchCanceledAndClosedOrders' => false,
                'fetchCanceledOrders' => true,
                'fetchClosedOrders' => true,
                'fetchCrossBorrowRate' => false,
                'fetchCrossBorrowRates' => false,
                'fetchCurrencies' => true,
                'fetchDepositAddress' => false,
                'fetchDepositAddresses' => false,
                'fetchDeposits' => true,
                'fetchDepositWithdrawFee' => 'emulated',
                'fetchDepositWithdrawFees' => false,
                'fetchFundingHistory' => true,
                'fetchFundingRate' => true,
                'fetchFundingRateHistory' => true,
                'fetchFundingRates' => false,
                'fetchIndexOHLCV' => false,
                'fetchIsolatedBorrowRate' => false,
                'fetchIsolatedBorrowRates' => false,
                'fetchLedger' => true,
                'fetchLeverage' => false,
                'fetchLeverageTiers' => false,
                'fetchLiquidations' => false,
                'fetchMarginMode' => null,
                'fetchMarketLeverageTiers' => false,
                'fetchMarkets' => true,
                'fetchMarkOHLCV' => false,
                'fetchMyLiquidations' => false,
                'fetchMyTrades' => true,
                'fetchOHLCV' => false,
                'fetchOpenInterest' => false,
                'fetchOpenInterestHistory' => false,
                'fetchOpenInterests' => false,
                'fetchOpenOrders' => true,
                'fetchOrder' => false,
                'fetchOrderBook' => false,
                'fetchOrders' => true,
                'fetchOrderTrades' => true,
                'fetchPosition' => false,
                'fetchPositionMode' => false,
                'fetchPositions' => true,
                'fetchPositionsRisk' => false,
                'fetchPremiumIndexOHLCV' => false,
                'fetchTicker' => true,
                'fetchTickers' => false,
                'fetchTime' => true,
                'fetchTrades' => true,
                'fetchTradingFee' => false,
                'fetchTradingFees' => false,
                'fetchTransfer' => false,
                'fetchTransfers' => false,
                'fetchWithdrawal' => false,
                'fetchWithdrawals' => true,
                'reduceMargin' => false,
                'repayCrossMargin' => false,
                'repayIsolatedMargin' => false,
                'sandbox' => true,
                'setLeverage' => false,
                'setMarginMode' => false,
                'setPositionMode' => false,
                'transfer' => false,
                'withdraw' => false,
            ),
            'timeframes' => array(
                '1m' => '1m',
                '3m' => '3m',
                '5m' => '5m',
                '15m' => '15m',
                '30m' => '30m',
                '1h' => '1h',
                '2h' => '2h',
                '4h' => '4h',
                '8h' => '8h',
                '12h' => '12h',
                '1d' => '1d',
                '3d' => '3d',
                '1w' => '1w',
                '1M' => '1M',
            ),
            'hostname' => 'derive.xyz',
            'urls' => array(
                'logo' => 'https://github.com/user-attachments/assets/f835b95f-033a-43dd-b6bb-24e698fc498c',
                'api' => array(
                    'public' => 'https://api.lyra.finance/public',
                    'private' => 'https://api.lyra.finance/private',
                ),
                'test' => array(
                    'public' => 'https://api-demo.lyra.finance/public',
                    'private' => 'https://api-demo.lyra.finance/private',
                ),
                'www' => 'https://www.derive.xyz/',
                'doc' => 'https://docs.derive.xyz/docs/',
                'fees' => 'https://docs.derive.xyz/reference/fees-1/',
                'referral' => 'https://www.derive.xyz/invite/3VB0B',
            ),
            'api' => array(
                'public' => array(
                    'get' => array(
                        'get_all_currencies',
                    ),
                    'post' => array(
                        'build_register_session_key_tx',
                        'register_session_key',
                        'deregister_session_key',
                        'login',
                        'statistics',
                        'get_all_currencies',
                        'get_currency',
                        'get_instrument',
                        'get_all_instruments',
                        'get_instruments',
                        'get_ticker',
                        'get_latest_signed_feeds',
                        'get_option_settlement_prices',
                        'get_spot_feed_history',
                        'get_spot_feed_history_candles',
                        'get_funding_rate_history',
                        'get_trade_history',
                        'get_option_settlement_history',
                        'get_liquidation_history',
                        'get_interest_rate_history',
                        'get_transaction',
                        'get_margin',
                        'margin_watch',
                        'validate_invite_code',
                        'get_points',
                        'get_all_points',
                        'get_points_leaderboard',
                        'get_descendant_tree',
                        'get_tree_roots',
                        'get_swell_percent_points',
                        'get_vault_assets',
                        'get_etherfi_effective_balances',
                        'get_kelp_effective_balances',
                        'get_bridge_balances',
                        'get_ethena_participants',
                        'get_vault_share',
                        'get_vault_statistics',
                        'get_vault_balances',
                        'estimate_integrator_points',
                        'create_subaccount_debug',
                        'deposit_debug',
                        'withdraw_debug',
                        'send_quote_debug',
                        'execute_quote_debug',
                        'get_invite_code',
                        'register_invite',
                        'get_time',
                        'get_live_incidents',
                        'get_maker_programs',
                        'get_maker_program_scores',
                    ),
                ),
                'private' => array(
                    'post' => array(
                        'get_account',
                        'create_subaccount',
                        'get_subaccount',
                        'get_subaccounts',
                        'get_all_portfolios',
                        'change_subaccount_label',
                        'get_notificationsv',
                        'update_notifications',
                        'deposit',
                        'withdraw',
                        'transfer_erc20',
                        'transfer_position',
                        'transfer_positions',
                        'order',
                        'replace',
                        'order_debug',
                        'get_order',
                        'get_orders',
                        'get_open_orders',
                        'cancel',
                        'cancel_by_label',
                        'cancel_by_nonce',
                        'cancel_by_instrument',
                        'cancel_all',
                        'cancel_trigger_order',
                        'get_order_history',
                        'get_trade_history',
                        'get_deposit_history',
                        'get_withdrawal_history',
                        'send_rfq',
                        'cancel_rfq',
                        'cancel_batch_rfqs',
                        'get_rfqs',
                        'poll_rfqs',
                        'send_quote',
                        'cancel_quote',
                        'cancel_batch_quotes',
                        'get_quotes',
                        'poll_quotes',
                        'execute_quote',
                        'rfq_get_best_quote',
                        'get_margin',
                        'get_collaterals',
                        'get_positions',
                        'get_option_settlement_history',
                        'get_subaccount_value_history',
                        'expired_and_cancelled_history',
                        'get_funding_history',
                        'get_interest_history',
                        'get_erc20_transfer_history',
                        'get_liquidation_history',
                        'liquidate',
                        'get_liquidator_history',
                        'session_keys',
                        'edit_session_key',
                        'register_scoped_session_key',
                        'get_mmp_config',
                        'set_mmp_config',
                        'reset_mmp',
                        'set_cancel_on_disconnect',
                        'get_invite_code',
                        'register_invite',
                    ),
                ),
            ),
            'fees' => array(
            ),
            'requiredCredentials' => array(
                'apiKey' => false,
                'secret' => false,
                'walletAddress' => true,
                'privateKey' => true,
            ),
            'exceptions' => array(
                'exact' => array(
                    '-32000' => '\\ccxt\\RateLimitExceeded', // Rate limit exceeded
                    '-32100' => '\\ccxt\\RateLimitExceeded', // Number of concurrent websocket clients limit exceeded
                    '-32700' => '\\ccxt\\BadRequest', // Parse error
                    '-32600' => '\\ccxt\\BadRequest', // Invalid Request
                    '-32601' => '\\ccxt\\BadRequest', // Method not found
                    '-32602' => '\\ccxt\\InvalidOrder', // array("id":"55e66a3d-6a4e-4a36-a23d-5cf8a91ef478","error":array("code":"","message":"Invalid params"))
                    '-32603' => '\\ccxt\\InvalidOrder', // array("code":"-32603","message":"Internal error","data":"SubAccount matching query does not exist.")
                    '9000' => '\\ccxt\\InvalidOrder', // Order confirmation timeout
                    '10000' => '\\ccxt\\BadRequest', // Manager not found
                    '10001' => '\\ccxt\\BadRequest', // Asset is not an ERC20 token
                    '10002' => '\\ccxt\\BadRequest', // Sender and recipient wallet do not match
                    '10003' => '\\ccxt\\BadRequest', // Sender and recipient subaccount IDs are the same
                    '10004' => '\\ccxt\\InvalidOrder', // Multiple currencies not supported
                    '10005' => '\\ccxt\\BadRequest', // Maximum number of subaccounts per wallet reached
                    '10006' => '\\ccxt\\BadRequest', // Maximum number of session keys per wallet reached
                    '10007' => '\\ccxt\\BadRequest', // Maximum number of assets per subaccount reached
                    '10008' => '\\ccxt\\BadRequest', // Maximum number of expiries per subaccount reached
                    '10009' => '\\ccxt\\BadRequest', // Recipient subaccount ID of the transfer cannot be 0
                    '10010' => '\\ccxt\\InvalidOrder', // PMRM only supports USDC asset collateral. Cannot trade spot markets.
                    '10011' => '\\ccxt\\InsufficientFunds', // ERC20 allowance is insufficient
                    '10012' => '\\ccxt\\InsufficientFunds', // ERC20 balance is less than transfer amount
                    '10013' => '\\ccxt\\ExchangeError', // There is a pending deposit for this asset
                    '10014' => '\\ccxt\\ExchangeError', // There is a pending withdrawal for this asset
                    '11000' => '\\ccxt\\InsufficientFunds', // Insufficient funds
                    '11002' => '\\ccxt\\InvalidOrder', // Order rejected from queue
                    '11003' => '\\ccxt\\InvalidOrder', // Already cancelled
                    '11004' => '\\ccxt\\InvalidOrder', // Already filled
                    '11005' => '\\ccxt\\InvalidOrder', // Already expired
                    '11006' => '\\ccxt\\OrderNotFound', // array("code":"11006","message":"Does not exist","data":"Open order with id => 804018f3-b092-40a3-a933-b29574fa1ff8 does not exist.")
                    '11007' => '\\ccxt\\InvalidOrder', // Self-crossing disallowed
                    '11008' => '\\ccxt\\InvalidOrder', // Post-only reject
                    '11009' => '\\ccxt\\InvalidOrder', // Zero liquidity for market or IOC/FOK order
                    '11010' => '\\ccxt\\InvalidOrder', // Post-only invalid order type
                    '11011' => '\\ccxt\\InvalidOrder', // array("code":11011,"message":"Invalid signature expiry","data":"Order must expire in 300 sec or more")
                    '11012' => '\\ccxt\\InvalidOrder', // array("code":"11012","message":"Invalid amount","data":"Amount must be a multiple of 0.01")
                    '11013' => '\\ccxt\\InvalidOrder', // array("code":"11013","message":"Invalid limit price","data":array("limit":"10000","bandwidth":"92530"))
                    '11014' => '\\ccxt\\InvalidOrder', // Fill-or-kill not filled
                    '11015' => '\\ccxt\\InvalidOrder', // MMP frozen
                    '11016' => '\\ccxt\\InvalidOrder', // Already consumed
                    '11017' => '\\ccxt\\InvalidOrder', // Non unique nonce
                    '11018' => '\\ccxt\\InvalidOrder', // Invalid nonce date
                    '11019' => '\\ccxt\\InvalidOrder', // Open orders limit exceeded
                    '11020' => '\\ccxt\\InsufficientFunds', // Negative ERC20 balance
                    '11021' => '\\ccxt\\InvalidOrder', // Instrument is not live
                    '11022' => '\\ccxt\\InvalidOrder', // Reject timestamp exceeded
                    '11023' => '\\ccxt\\InvalidOrder', // array("code":"11023","message":"Max fee order param is too low","data":"signed max_fee must be >= 194.420835871999983091712000000000000000")
                    '11024' => '\\ccxt\\InvalidOrder', // array("code":11024,"message":"Reduce only not supported with this time in force")
                    '11025' => '\\ccxt\\InvalidOrder', // Reduce only reject
                    '11026' => '\\ccxt\\BadRequest', // Transfer reject
                    '11027' => '\\ccxt\\InvalidOrder', // Subaccount undergoing liquidation
                    '11028' => '\\ccxt\\InvalidOrder', // Replaced order filled amount does not match expected state.
                    '11050' => '\\ccxt\\InvalidOrder', // Trigger order was cancelled between the time worker sent order and engine processed order
                    '11051' => '\\ccxt\\InvalidOrder', // array("code":"11051","message":"Trigger price must be higher than the current price for stop orders and vice versa for take orders","data":"Trigger price 9000.0 must be < or > current price 102671.2 depending on trigger type and direction.")
                    '11052' => '\\ccxt\\InvalidOrder', // Trigger order limit exceeded (separate limit from regular orders)
                    '11053' => '\\ccxt\\InvalidOrder', // Index and last-trade trigger price types not supported yet
                    '11054' => '\\ccxt\\InvalidOrder', // array("code":"11054","message":"Trigger orders cannot replace or be replaced")
                    '11055' => '\\ccxt\\InvalidOrder', // Market order limit_price is unfillable at the given trigger price
                    '11100' => '\\ccxt\\InvalidOrder', // Leg instruments are not unique
                    '11101' => '\\ccxt\\InvalidOrder', // RFQ not found
                    '11102' => '\\ccxt\\InvalidOrder', // Quote not found
                    '11103' => '\\ccxt\\InvalidOrder', // Quote leg does not match RFQ leg
                    '11104' => '\\ccxt\\InvalidOrder', // Requested quote or RFQ is not open
                    '11105' => '\\ccxt\\InvalidOrder', // Requested quote ID references a different RFQ ID
                    '11106' => '\\ccxt\\InvalidOrder', // Invalid RFQ counterparty
                    '11107' => '\\ccxt\\InvalidOrder', // Quote maker total cost too high
                    '11200' => '\\ccxt\\InvalidOrder', // Auction not ongoing
                    '11201' => '\\ccxt\\InvalidOrder', // Open orders not allowed
                    '11202' => '\\ccxt\\InvalidOrder', // Price limit exceeded
                    '11203' => '\\ccxt\\InvalidOrder', // Last trade ID mismatch
                    '12000' => '\\ccxt\\InvalidOrder', // Asset not found
                    '12001' => '\\ccxt\\InvalidOrder', // Instrument not found
                    '12002' => '\\ccxt\\BadRequest', // Currency not found
                    '12003' => '\\ccxt\\BadRequest', // USDC does not have asset caps per manager
                    '13000' => '\\ccxt\\BadRequest', // Invalid channels
                    '14000' => '\\ccxt\\BadRequest', // array("code" => 14000, "message" => "Account not found")
                    '14001' => '\\ccxt\\InvalidOrder', // array("code" => 14001, "message" => "Subaccount not found")
                    '14002' => '\\ccxt\\BadRequest', // Subaccount was withdrawn
                    '14008' => '\\ccxt\\BadRequest', // Cannot reduce expiry using registerSessionKey RPC route
                    '14009' => '\\ccxt\\BadRequest', // Session key expiry must be > utc_now + 10 min
                    '14010' => '\\ccxt\\BadRequest', // Session key already registered for this account
                    '14011' => '\\ccxt\\BadRequest', // Session key already registered with another account
                    '14012' => '\\ccxt\\BadRequest', // Address must be checksummed
                    '14013' => '\\ccxt\\BadRequest', // 'strval' is not a valid ethereum address
                    '14014' => '\\ccxt\\InvalidOrder', // array("code":"14014","message":"Signature invalid for message or transaction","data":"Signature does not match data")
                    '14015' => '\\ccxt\\BadRequest', // Transaction count for given wallet does not match provided nonce
                    '14016' => '\\ccxt\\BadRequest', // The provided signed raw transaction contains function name that does not match the expected function name
                    '14017' => '\\ccxt\\BadRequest', // The provided signed raw transaction contains contract address that does not match the expected contract address
                    '14018' => '\\ccxt\\BadRequest', // The provided signed raw transaction contains function params that do not match any expected function params
                    '14019' => '\\ccxt\\BadRequest', // The provided signed raw transaction contains function param values that do not match the expected values
                    '14020' => '\\ccxt\\BadRequest', // The X-LyraWallet header does not match the requested subaccount_id or wallet
                    '14021' => '\\ccxt\\BadRequest', // The X-LyraWallet header not provided
                    '14022' => '\\ccxt\\AuthenticationError', // Subscription to a private channel failed
                    '14023' => '\\ccxt\\InvalidOrder', // array(is_array(on-chain related request is not wallet owner or registered session key","data":"Session key does not belong to wallet") && array_key_exists("code":"14023","message":"Signer, on-chain related request is not wallet owner or registered session key","data":"Session key does not belong to wallet"))
                    '14024' => '\\ccxt\\BadRequest', // Chain ID must match the current roll up chain id
                    '14025' => '\\ccxt\\BadRequest', // The private request is missing a wallet or subaccount_id param
                    '14026' => '\\ccxt\\BadRequest', // Session key not found
                    '14027' => '\\ccxt\\AuthenticationError', // Unauthorized maker
                    '14028' => '\\ccxt\\BadRequest', // Cross currency RFQ not supported
                    '14029' => '\\ccxt\\AuthenticationError', // Session key IP not whitelisted
                    '14030' => '\\ccxt\\BadRequest', // Session key expired
                    '14031' => '\\ccxt\\AuthenticationError', // Unauthorized key scope
                    '14032' => '\\ccxt\\BadRequest', // Scope should not be changed
                    '16000' => '\\ccxt\\AuthenticationError', // You are in a restricted region that violates our terms of service.
                    '16001' => '\\ccxt\\AuthenticationError', // Account is disabled due to compliance violations, please contact support to enable it.
                    '16100' => '\\ccxt\\AuthenticationError', // Sentinel authorization is invalid
                    '17000' => '\\ccxt\\BadRequest', // This accoount does not have a shareable invite code
                    '17001' => '\\ccxt\\BadRequest', // Invalid invite code
                    '17002' => '\\ccxt\\BadRequest', // Invite code already registered for this account
                    '17003' => '\\ccxt\\BadRequest', // Invite code has no remaining uses
                    '17004' => '\\ccxt\\BadRequest', // Requirement for successful invite registration not met
                    '17005' => '\\ccxt\\BadRequest', // Account must register with a valid invite code to be elligible for points
                    '17006' => '\\ccxt\\BadRequest', // Point program does not exist
                    '17007' => '\\ccxt\\BadRequest', // Invalid leaderboard page number
                    '18000' => '\\ccxt\\BadRequest', // Invalid block number
                    '18001' => '\\ccxt\\BadRequest', // Failed to estimate block number. Please try again later.
                    '18002' => '\\ccxt\\BadRequest', // The provided smart contract owner does not match the wallet in LightAccountFactory.getAddress()
                    '18003' => '\\ccxt\\BadRequest', // Vault ERC20 asset does not exist
                    '18004' => '\\ccxt\\BadRequest', // Vault ERC20 pool does not exist
                    '18005' => '\\ccxt\\BadRequest', // Must add asset to pool before getting balances
                    '18006' => '\\ccxt\\BadRequest', // Invalid Swell season. Swell seasons are in the form 'swell_season_X'.
                    '18007' => '\\ccxt\\BadRequest', // Vault not found
                    '19000' => '\\ccxt\\BadRequest', // Maker program not found
                ),
                'broad' => array(
                ),
            ),
            'precisionMode' => TICK_SIZE,
            'commonCurrencies' => array(
            ),
            'options' => array(
                'deriveWalletAddress' => '', // a derive wallet address "0x"-prefixed hexstring
                'id' => '0x0ad42b8e602c2d3d475ae52d678cf63d84ab2749',
            ),
        ));
    }

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

    public function fetch_time($params = array ()) {
        return Async\async(function () use ($params) {
            /**
             * fetches the current integer timestamp in milliseconds from the exchange server
             *
             * @see https://docs.derive.xyz/reference/post_public-get-time
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {int} the current integer timestamp in milliseconds from the exchange server
             */
            $response = Async\await($this->publicPostGetTime ($params));
            //
            // {
            //     "result" => 1735846536758,
            //     "id" => "f1c03d21-f886-4c5a-9a9d-33dd06f180f0"
            // }
            //
            return $this->safe_integer($response, 'result');
        }) ();
    }

    public function fetch_currencies($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * fetches all available $currencies on an exchange
             *
             * @see https://docs.derive.xyz/reference/post_public-get-all-$currencies
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} an associative dictionary of $currencies
             */
            $result = array();
            $tokenResponse = Async\await($this->publicGetGetAllCurrencies ($params));
            //
            //    {
            //        "result" => array(
            //            {
            //                "currency" => "SEI",
            //                "instrument_types" => [
            //                    "perp"
            //                ),
            //                "protocol_asset_addresses" => array(
            //                    "perp" => "0x7225889B75fd34C68eA3098dAE04D50553C09840",
            //                    "option" => null,
            //                    "spot" => null,
            //                    "underlying_erc20" => null
            //                ),
            //                "managers" => array(
            //                    {
            //                        "address" => "0x28c9ddF9A3B29c2E6a561c1BC520954e5A33de5D",
            //                        "margin_type" => "SM",
            //                        "currency" => null
            //                    }
            //                ),
            //                "srm_im_discount" => "0",
            //                "srm_mm_discount" => "0",
            //                "pm2_collateral_discounts" => array(),
            //                "borrow_apy" => "0",
            //                "supply_apy" => "0",
            //                "total_borrow" => "0",
            //                "total_supply" => "0",
            //                "asset_cap_and_supply_per_manager" => array(
            //                    "perp" => array(
            //                        "SM" => array(
            //                            array(
            //                                "current_open_interest" => "0",
            //                                "interest_cap" => "2000000",
            //                                "manager_currency" => null
            //                            }
            //                        )
            //                    ),
            //                    "option" => array(),
            //                    "erc20" => array()
            //                ),
            //                "market_type" => "SRM_PERP_ONLY",
            //                "spot_price" => "0.2193542905042081",
            //                "spot_price_24h" => "0.238381655533635830"
            //            ),
            //     "id" => "7e07fe1d-0ab4-4d2b-9e22-b65ce9e232dc"
            // }
            //
            $currencies = $this->safe_list($tokenResponse, 'result', array());
            for ($i = 0; $i < count($currencies); $i++) {
                $currency = $currencies[$i];
                $currencyId = $this->safe_string($currency, 'currency');
                $code = $this->safe_currency_code($currencyId);
                $result[$code] = $this->safe_currency_structure(array(
                    'id' => $currencyId,
                    'name' => null,
                    'code' => $code,
                    'precision' => null,
                    'active' => null,
                    'fee' => null,
                    'networks' => null,
                    'deposit' => null,
                    'withdraw' => null,
                    'limits' => array(
                        'deposit' => array(
                            'min' => null,
                            'max' => null,
                        ),
                        'withdraw' => array(
                            'min' => null,
                            'max' => null,
                        ),
                    ),
                    'info' => $currency,
                ));
            }
            return $result;
        }) ();
    }

    public function fetch_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * retrieves data on all markets for bybit
             *
             * @see https://docs.derive.xyz/reference/post_public-get-all-instruments
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} an array of objects representing market data
             */
            $spotMarketsPromise = $this->fetch_spot_markets($params);
            $swapMarketsPromise = $this->fetch_swap_markets($params);
            $optionMarketsPromise = $this->fetch_option_markets($params);
            list($spotMarkets, $swapMarkets, $optionMarkets) = Async\await(Promise\all(array( $spotMarketsPromise, $swapMarketsPromise, $optionMarketsPromise )));
            //
            // {
            //     "result" => {
            //         "instruments" => array(
            //             {
            //                 "instrument_type" => "perp",
            //                 "instrument_name" => "BTC-PERP",
            //                 "scheduled_activation" => 1701840228,
            //                 "scheduled_deactivation" => 9223372036854776000,
            //                 "is_active" => true,
            //                 "tick_size" => "0.1",
            //                 "minimum_amount" => "0.01",
            //                 "maximum_amount" => "10000",
            //                 "amount_step" => "0.001",
            //                 "mark_price_fee_rate_cap" => "0",
            //                 "maker_fee_rate" => "0.00005",
            //                 "taker_fee_rate" => "0.0003",
            //                 "base_fee" => "0.1",
            //                 "base_currency" => "BTC",
            //                 "quote_currency" => "USD",
            //                 "option_details" => null,
            //                 "perp_details" => array(
            //                     "index" => "BTC-USD",
            //                     "max_rate_per_hour" => "0.004",
            //                     "min_rate_per_hour" => "-0.004",
            //                     "static_interest_rate" => "0.0000125",
            //                     "aggregate_funding" => "10538.574363381759146829",
            //                     "funding_rate" => "0.0000125"
            //                 ),
            //                 "erc20_details" => null,
            //                 "base_asset_address" => "0xDBa83C0C654DB1cd914FA2710bA743e925B53086",
            //                 "base_asset_sub_id" => "0",
            //                 "pro_rata_fraction" => "0",
            //                 "fifo_min_allocation" => "0",
            //                 "pro_rata_amount_step" => "0.1"
            //             }
            //         ),
            //         "pagination" => array(
            //             "num_pages" => 1,
            //             "count" => 1
            //         }
            //     ),
            //     "id" => "a06bc0b2-8e78-4536-a21f-f785f225b5a5"
            // }
            //
            $result = $this->array_concat($spotMarkets, $swapMarkets);
            $result = $this->array_concat($result, $optionMarkets);
            return $result;
        }) ();
    }

    public function fetch_spot_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            $request = array(
                'expired' => false,
                'instrument_type' => 'erc20',
            );
            $response = Async\await($this->publicPostGetAllInstruments ($this->extend($request, $params)));
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'instruments', array());
            return $this->parse_markets($data);
        }) ();
    }

    public function fetch_swap_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            $request = array(
                'expired' => false,
                'instrument_type' => 'perp',
            );
            $response = Async\await($this->publicPostGetAllInstruments ($this->extend($request, $params)));
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'instruments', array());
            return $this->parse_markets($data);
        }) ();
    }

    public function fetch_option_markets($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            $request = array(
                'expired' => false,
                'instrument_type' => 'option',
            );
            $response = Async\await($this->publicPostGetAllInstruments ($this->extend($request, $params)));
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'instruments', array());
            return $this->parse_markets($data);
        }) ();
    }

    public function parse_market(array $market): array {
        $type = $this->safe_string($market, 'instrument_type');
        $spot = false;
        $margin = true;
        $swap = false;
        $option = false;
        $linear = null;
        $inverse = null;
        $baseId = $this->safe_string($market, 'base_currency');
        $quoteId = $this->safe_string($market, 'quote_currency');
        $base = $this->safe_currency_code($baseId);
        $quote = $this->safe_currency_code($quoteId);
        $marketId = $this->safe_string($market, 'instrument_name');
        $symbol = $base . '/' . $quote;
        $settleId = null;
        $settle = null;
        $expiry = null;
        $strike = null;
        $optionType = null;
        $optionLetter = null;
        if ($type === 'erc20') {
            $spot = true;
            $marketType = 'spot';
        } elseif ($type === 'perp') {
            $margin = false;
            $settleId = 'USDC';
            $settle = $this->safe_currency_code($settleId);
            $symbol = $base . '/' . $quote . ':' . $settle;
            $swap = true;
            $linear = true;
            $inverse = false;
            $marketType = 'swap';
        } elseif ($type === 'option') {
            $settleId = 'USDC';
            $settle = $this->safe_currency_code($settleId);
            $margin = false;
            $option = true;
            $marketType = 'option';
            $optionDetails = $this->safe_dict($market, 'option_details');
            $expiry = $this->safe_timestamp($optionDetails, 'expiry');
            $strike = $this->safe_integer($optionDetails, 'strike');
            $optionLetter = $this->safe_string($optionDetails, 'option_type');
            $symbol = $base . '/' . $quote . ':' . $settle . '-' . $this->yymmdd($expiry) . '-' . $this->number_to_string($strike) . '-' . $optionLetter;
            if ($optionLetter === 'P') {
                $optionType = 'put';
            } else {
                $optionType = 'call';
            }
            $linear = true;
            $inverse = false;
        }
        return $this->safe_market_structure(array(
            'id' => $marketId,
            'symbol' => $symbol,
            'base' => $base,
            'quote' => $quote,
            'settle' => $settle,
            'baseId' => $baseId,
            'quoteId' => $quoteId,
            'settleId' => $settleId,
            'type' => $marketType,
            'spot' => $spot,
            'margin' => $margin,
            'swap' => $swap,
            'future' => false,
            'option' => $option,
            'active' => $this->safe_bool($market, 'is_active'),
            'contract' => ($swap || $option),
            'linear' => $linear,
            'inverse' => $inverse,
            'contractSize' => ($spot) ? null : 1,
            'expiry' => $expiry,
            'expiryDatetime' => $this->iso8601($expiry),
            'taker' => $this->safe_number($market, 'taker_fee_rate'),
            'maker' => $this->safe_number($market, 'maker_fee_rate'),
            'strike' => $strike,
            'optionType' => $optionType,
            'precision' => array(
                'amount' => $this->safe_number($market, 'amount_step'),
                'price' => $this->safe_number($market, 'tick_size'),
            ),
            'limits' => array(
                'leverage' => array(
                    'min' => null,
                    'max' => null,
                ),
                'amount' => array(
                    'min' => $this->safe_number($market, 'minimum_amount'),
                    'max' => $this->safe_number($market, 'maximum_amount'),
                ),
                'price' => array(
                    'min' => null,
                    'max' => null,
                ),
                'cost' => array(
                    'min' => null,
                    'max' => null,
                ),
            ),
            'created' => null,
            'info' => $market,
        ));
    }

    public function fetch_ticker(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific $market
             *
             * @see https://docs.derive.xyz/reference/post_public-get-ticker
             *
             * @param {string} $symbol unified $symbol of the $market to fetch the ticker for
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=ticker-structure ticker structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'instrument_name' => $market['id'],
            );
            $response = Async\await($this->publicPostGetTicker ($this->extend($request, $params)));
            //
            // spot
            //
            // {
            //     "result" => array(
            //         "instrument_type" => "perp",
            //         "instrument_name" => "BTC-PERP",
            //         "scheduled_activation" => 1701840228,
            //         "scheduled_deactivation" => 9223372036854776000,
            //         "is_active" => true,
            //         "tick_size" => "0.1",
            //         "minimum_amount" => "0.01",
            //         "maximum_amount" => "10000",
            //         "amount_step" => "0.001",
            //         "mark_price_fee_rate_cap" => "0",
            //         "maker_fee_rate" => "0.00005",
            //         "taker_fee_rate" => "0.0003",
            //         "base_fee" => "0.1",
            //         "base_currency" => "BTC",
            //         "quote_currency" => "USD",
            //         "option_details" => null,
            //         "perp_details" => array(
            //             "index" => "BTC-USD",
            //             "max_rate_per_hour" => "0.004",
            //             "min_rate_per_hour" => "-0.004",
            //             "static_interest_rate" => "0.0000125",
            //             "aggregate_funding" => "10512.580833189805742522",
            //             "funding_rate" => "-0.000022223906766867"
            //         ),
            //         "erc20_details" => null,
            //         "base_asset_address" => "0xDBa83C0C654DB1cd914FA2710bA743e925B53086",
            //         "base_asset_sub_id" => "0",
            //         "pro_rata_fraction" => "0",
            //         "fifo_min_allocation" => "0",
            //         "pro_rata_amount_step" => "0.1",
            //         "best_ask_amount" => "0.012",
            //         "best_ask_price" => "99567.9",
            //         "best_bid_amount" => "0.129",
            //         "best_bid_price" => "99554.5",
            //         "five_percent_bid_depth" => "11.208",
            //         "five_percent_ask_depth" => "11.42",
            //         "option_pricing" => null,
            //         "index_price" => "99577.2",
            //         "mark_price" => "99543.642926357933902181684970855712890625",
            //         "stats" => array(
            //             "contract_volume" => "464.712",
            //             "num_trades" => "10681",
            //             "open_interest" => "72.804739389481989861",
            //             "high" => "99519.1",
            //             "low" => "97254.1",
            //             "percent_change" => "0.0128",
            //             "usd_change" => "1258.1"
            //         ),
            //         "timestamp" => 1736140984000,
            //         "min_price" => "97591.2",
            //         "max_price" => "101535.1"
            //     ),
            //     "id" => "bbd7c271-c2be-48f7-b93a-26cf6d4cb79f"
            // }
            //
            $data = $this->safe_dict($response, 'result', array());
            return $this->parse_ticker($data, $market);
        }) ();
    }

    public function parse_ticker(array $ticker, ?array $market = null): array {
        //
        // {
        //     "instrument_type" => "perp",
        //     "instrument_name" => "BTC-PERP",
        //     "scheduled_activation" => 1701840228,
        //     "scheduled_deactivation" => 9223372036854776000,
        //     "is_active" => true,
        //     "tick_size" => "0.1",
        //     "minimum_amount" => "0.01",
        //     "maximum_amount" => "10000",
        //     "amount_step" => "0.001",
        //     "mark_price_fee_rate_cap" => "0",
        //     "maker_fee_rate" => "0.00005",
        //     "taker_fee_rate" => "0.0003",
        //     "base_fee" => "0.1",
        //     "base_currency" => "BTC",
        //     "quote_currency" => "USD",
        //     "option_details" => null,
        //     "perp_details" => array(
        //         "index" => "BTC-USD",
        //         "max_rate_per_hour" => "0.004",
        //         "min_rate_per_hour" => "-0.004",
        //         "static_interest_rate" => "0.0000125",
        //         "aggregate_funding" => "10512.580833189805742522",
        //         "funding_rate" => "-0.000022223906766867"
        //     ),
        //     "erc20_details" => null,
        //     "base_asset_address" => "0xDBa83C0C654DB1cd914FA2710bA743e925B53086",
        //     "base_asset_sub_id" => "0",
        //     "pro_rata_fraction" => "0",
        //     "fifo_min_allocation" => "0",
        //     "pro_rata_amount_step" => "0.1",
        //     "best_ask_amount" => "0.012",
        //     "best_ask_price" => "99567.9",
        //     "best_bid_amount" => "0.129",
        //     "best_bid_price" => "99554.5",
        //     "five_percent_bid_depth" => "11.208",
        //     "five_percent_ask_depth" => "11.42",
        //     "option_pricing" => null,
        //     "index_price" => "99577.2",
        //     "mark_price" => "99543.642926357933902181684970855712890625",
        //     "stats" => array(
        //         "contract_volume" => "464.712",
        //         "num_trades" => "10681",
        //         "open_interest" => "72.804739389481989861",
        //         "high" => "99519.1",
        //         "low" => "97254.1",
        //         "percent_change" => "0.0128",
        //         "usd_change" => "1258.1"
        //     ),
        //     "timestamp" => 1736140984000,
        //     "min_price" => "97591.2",
        //     "max_price" => "101535.1"
        // }
        //
        $marketId = $this->safe_string($ticker, 'instrument_name');
        $timestamp = $this->safe_integer_omit_zero($ticker, 'timestamp');
        $symbol = $this->safe_symbol($marketId, $market);
        $stats = $this->safe_dict($ticker, 'stats');
        $change = $this->safe_string($stats, 'percent_change');
        return $this->safe_ticker(array(
            'symbol' => $symbol,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'high' => $this->safe_string($stats, 'high'),
            'low' => $this->safe_string($stats, 'low'),
            'bid' => $this->safe_string($ticker, 'best_bid_price'),
            'bidVolume' => $this->safe_string($ticker, 'best_bid_amount'),
            'ask' => $this->safe_string($ticker, 'best_ask_price'),
            'askVolume' => $this->safe_string($ticker, 'best_ask_amount'),
            'vwap' => null,
            'open' => null,
            'close' => null,
            'last' => null,
            'previousClose' => null,
            'change' => $change,
            'percentage' => Precise::string_mul($change, '100'),
            'average' => null,
            'baseVolume' => null,
            'quoteVolume' => null,
            'indexPrice' => $this->safe_string($ticker, 'index_price'),
            'markPrice' => $this->safe_string($ticker, 'mark_price'),
            'info' => $ticker,
        ), $market);
    }

    public function fetch_trades(string $symbol, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * get the list of most recent trades for a particular $symbol
             *
             * @see https://docs.derive.xyz/reference/post_public-get-trade-history
             *
             * @param {string} $symbol unified $symbol of the $market to fetch trades for
             * @param {int} [$since] timestamp in ms of the earliest trade to fetch
             * @param {int} [$limit] the maximum amount of trades to fetch
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {int} [$params->until] the latest time in ms to fetch trades for
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=public-trades trade structures~
             */
            Async\await($this->load_markets());
            $request = array();
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['instrument_name'] = $market['id'];
            }
            if ($limit !== null) {
                if ($limit > 1000) {
                    $limit = 1000;
                }
                $request['page_size'] = $limit; // default 100, max 1000
            }
            if ($since !== null) {
                $request['from_timestamp'] = $since;
            }
            $until = $this->safe_integer($params, 'until');
            $params = $this->omit($params, array( 'until' ));
            if ($until !== null) {
                $request['to_timestamp'] = $until;
            }
            $response = Async\await($this->publicPostGetTradeHistory ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "trades" => array(
            //             {
            //                 "trade_id" => "9dbc88b0-f0c4-4439-9cc1-4e6409d4eafb",
            //                 "instrument_name" => "BTC-PERP",
            //                 "timestamp" => 1736153910930,
            //                 "trade_price" => "98995.3",
            //                 "trade_amount" => "0.033",
            //                 "mark_price" => "98990.875914388161618263",
            //                 "index_price" => "99038.050611100001501184",
            //                 "direction" => "sell",
            //                 "quote_id" => null,
            //                 "wallet" => "0x88B6BB87fbFac92a34F8155aaA35c87B5b166fA9",
            //                 "subaccount_id" => 8250,
            //                 "tx_status" => "settled",
            //                 "tx_hash" => "0x020bd735b312f867f17f8cc254946d87cfe9f2c8ff3605035d8129082eb73723",
            //                 "trade_fee" => "0.980476701049890015",
            //                 "liquidity_role" => "taker",
            //                 "realized_pnl" => "-2.92952402688793509",
            //                 "realized_pnl_excl_fees" => "-1.949047325838045075"
            //             }
            //         ),
            //         "pagination" => array(
            //             "num_pages" => 598196,
            //             "count" => 598196
            //         }
            //     ),
            //     "id" => "b8539544-6975-4497-8163-5e51a38e4aa7"
            // }
            //
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'trades', array());
            return $this->parse_trades($data, $market, $since, $limit);
        }) ();
    }

    public function parse_trade(array $trade, ?array $market = null): array {
        //
        // {
        //     "subaccount_id" => 130837,
        //     "order_id" => "30c48194-8d48-43ac-ad00-0d5ba29eddc9",
        //     "instrument_name" => "BTC-PERP",
        //     "direction" => "sell",
        //     "label" => "test1234",
        //     "quote_id" => null,
        //     "trade_id" => "f8a30740-488c-4c2d-905d-e17057bafde1",
        //     "timestamp" => 1738065303708,
        //     "mark_price" => "102740.137375457314192317",
        //     "index_price" => "102741.553409299981533184",
        //     "trade_price" => "102700.6",
        //     "trade_amount" => "0.01",
        //     "liquidity_role" => "taker",
        //     "realized_pnl" => "0",
        //     "realized_pnl_excl_fees" => "0",
        //     "is_transfer" => false,
        //     "tx_status" => "settled",
        //     "trade_fee" => "1.127415534092999815",
        //     "tx_hash" => "0xc55df1f07330faf86579bd8a6385391fbe9e73089301149d8550e9d29c9ead74",
        //     "transaction_id" => "e18b9426-3fa5-41bb-99d3-8b54fb4d51bb"
        // }
        //
        $marketId = $this->safe_string($trade, 'instrument_name');
        $symbol = $this->safe_symbol($marketId, $market);
        $timestamp = $this->safe_integer($trade, 'timestamp');
        $fee = array(
            'currency' => 'USDC',
            'cost' => $this->safe_string($trade, 'trade_fee'),
        );
        return $this->safe_trade(array(
            'info' => $trade,
            'id' => $this->safe_string($trade, 'trade_id'),
            'order' => $this->safe_string($trade, 'order_id'),
            'symbol' => $symbol,
            'side' => $this->safe_string_lower($trade, 'direction'),
            'type' => null,
            'takerOrMaker' => $this->safe_string($trade, 'liquidity_role'),
            'price' => $this->safe_string($trade, 'trade_price'),
            'amount' => $this->safe_string($trade, 'trade_amount'),
            'cost' => null,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'fee' => $fee,
        ), $market);
    }

    public function fetch_funding_rate_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches historical funding rate prices
             *
             * @see https://docs.derive.xyz/reference/post_public-get-funding-rate-history
             *
             * @param {string} $symbol unified $symbol of the $market to fetch the funding rate history for
             * @param {int} [$since] $timestamp in ms of the earliest funding rate to fetch
             * @param {int} [$limit] the maximum amount of funding rate structures to fetch
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=funding-rate-history-structure funding rate structures~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $request = array(
                'instrument_name' => $market['id'],
            );
            if ($since !== null) {
                $request['start_timestamp'] = $since;
            }
            $until = $this->safe_integer($params, 'until');
            $params = $this->omit($params, array( 'until' ));
            if ($until !== null) {
                $request['to_timestamp'] = $until;
            }
            $response = Async\await($this->publicPostGetFundingRateHistory ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "funding_rate_history" => array(
            //             array(
            //                 "timestamp" => 1736215200000,
            //                 "funding_rate" => "-0.000020014"
            //             }
            //         )
            //     ),
            //     "id" => "3200ab8d-0080-42f0-8517-c13e3d9201d8"
            // }
            //
            $result = $this->safe_dict($response, 'result', array());
            $data = $this->safe_list($result, 'funding_rate_history', array());
            $rates = array();
            for ($i = 0; $i < count($data); $i++) {
                $entry = $data[$i];
                $timestamp = $this->safe_integer($entry, 'timestamp');
                $rates[] = array(
                    'info' => $entry,
                    'symbol' => $market['symbol'],
                    'fundingRate' => $this->safe_number($entry, 'funding_rate'),
                    'timestamp' => $timestamp,
                    'datetime' => $this->iso8601($timestamp),
                );
            }
            $sorted = $this->sort_by($rates, 'timestamp');
            return $this->filter_by_symbol_since_limit($sorted, $market['symbol'], $since, $limit);
        }) ();
    }

    public function fetch_funding_rate(string $symbol, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $params) {
            /**
             * fetch the current funding rate
             *
             * @see https://docs.derive.xyz/reference/post_public-get-funding-rate-history
             *
             * @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~
             */
            $response = Async\await($this->fetch_funding_rate_history($symbol, null, 1, $params));
            //
            // array(
            //     {
            //         "info" => array(
            //             "timestamp" => 1736157600000,
            //             "funding_rate" => "-0.000008872"
            //         ),
            //         "symbol" => "BTC/USD:USDC",
            //         "fundingRate" => -0.000008872,
            //         "timestamp" => 1736157600000,
            //         "datetime" => "2025-01-06T10:00:00.000Z"
            //     }
            // )
            //
            $data = $this->safe_dict($response, 0);
            return $this->parse_funding_rate($data);
        }) ();
    }

    public function parse_funding_rate($contract, ?array $market = null): array {
        $symbol = $this->safe_string($contract, 'symbol');
        $fundingTimestamp = $this->safe_integer($contract, 'timestamp');
        return array(
            'info' => $contract,
            'symbol' => $symbol,
            'markPrice' => null,
            'indexPrice' => null,
            'interestRate' => null,
            'estimatedSettlePrice' => null,
            'timestamp' => null,
            'datetime' => null,
            'fundingRate' => $this->safe_number($contract, 'fundingRate'),
            'fundingTimestamp' => $fundingTimestamp,
            'fundingDatetime' => $this->iso8601($fundingTimestamp),
            'nextFundingRate' => null,
            'nextFundingTimestamp' => null,
            'nextFundingDatetime' => null,
            'previousFundingRate' => null,
            'previousFundingTimestamp' => null,
            'previousFundingDatetime' => null,
            'interval' => null,
        );
    }

    public function hash_order_message($order) {
        $accountHash = $this->hash($this->eth_abi_encode(array(
            'bytes32', 'uint256', 'uint256', 'address', 'bytes32', 'uint256', 'address', 'address',
        ), $order), 'keccak', 'binary');
        $sandboxMode = $this->safe_bool($this->options, 'sandboxMode', false);
        $DOMAIN_SEPARATOR = ($sandboxMode) ? '9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105' : 'd96e5f90797da7ec8dc4e276260c7f3f87fedf68775fbe1ef116e996fc60441b';
        $binaryDomainSeparator = $this->base16_to_binary($DOMAIN_SEPARATOR);
        $prefix = $this->base16_to_binary('1901');
        return $this->hash($this->binary_concat($prefix, $binaryDomainSeparator, $accountHash), 'keccak', 'hex');
    }

    public function sign_order($order, $privateKey) {
        $hashOrder = $this->hash_order_message($order);
        return $this->sign_hash(mb_substr($hashOrder, -64), mb_substr($privateKey, -64));
    }

    public function hash_message($message) {
        $binaryMessage = $this->encode($message);
        $binaryMessageLength = $this->binary_length($binaryMessage);
        $x19 = $this->base16_to_binary('19');
        $newline = $this->base16_to_binary('0a');
        $prefix = $this->binary_concat($x19, $this->encode('Ethereum Signed Message:'), $newline, $this->encode($this->number_to_string($binaryMessageLength)));
        return '0x' . $this->hash($this->binary_concat($prefix, $binaryMessage), 'keccak', 'hex');
    }

    public function sign_hash($hash, $privateKey) {
        $this->check_required_credentials();
        $signature = $this->ecdsa(mb_substr($hash, -64), mb_substr($privateKey, -64), 'secp256k1', null);
        $r = $signature['r'];
        $s = $signature['s'];
        $v = $this->int_to_base16($this->sum(27, $signature['v']));
        return '0x' . str_pad($r, 64, '0', STR_PAD_LEFT) . str_pad($s, 64, '0', STR_PAD_LEFT) . $v;
    }

    public function sign_message($message, $privateKey) {
        return $this->sign_hash($this->hash_message($message), mb_substr($privateKey, -64));
    }

    public function parse_units(string $num, $dec = '1000000000000000000') {
        return Precise::string_mul($num, $dec);
    }

    public function create_order(string $symbol, string $type, string $side, float $amount, ?float $price = null, $params = array ()) {
        return Async\async(function () use ($symbol, $type, $side, $amount, $price, $params) {
            /**
             * create a trade $order
             *
             * @see https://docs.derive.xyz/reference/post_private-$order
             *
             * @param {string} $symbol unified $symbol of the $market to create an $order in
             * @param {string} $type 'market' or 'limit'
             * @param {string} $side 'buy' or 'sell'
             * @param {float} $amount how much of currency you want to trade in units of base currency
             * @param {float} [$price] the $price at which the $order is to be fulfilled, in units of the quote currency, ignored in $market orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subaccount_id] *required* the subaccount id
             * @param {float} [$params->triggerPrice] The $price a trigger $order is triggered at
             * @param {array} [$params->takeProfit] *$takeProfit object in $params* containing the triggerPrice at which the attached take profit $order will be triggered (perpetual swap markets only)
             * @param {float} [$params->takeProfit.triggerPrice] take profit trigger $price
             * @param {array} [$params->stopLoss] *$stopLoss object in $params* containing the triggerPrice at which the attached stop loss $order will be triggered (perpetual swap markets only)
             * @param {float} [$params->stopLoss.triggerPrice] stop loss trigger $price
             * @param {float} [$params->max_fee] *required* the maximum fee you are willing to pay for the $order
             * @return {array} an ~@link https://docs.ccxt.com/#/?id=$order-structure $order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            if ($price === null) {
                throw new ArgumentsRequired($this->id . ' createOrder() requires a $price argument');
            }
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('createOrder', $params);
            $test = $this->safe_bool($params, 'test', false);
            $reduceOnly = $this->safe_bool_2($params, 'reduceOnly', 'reduce_only');
            $timeInForce = $this->safe_string_lower_2($params, 'timeInForce', 'time_in_force');
            $postOnly = $this->safe_bool($params, 'postOnly');
            $orderType = strtolower($type);
            $orderSide = strtolower($side);
            $nonce = $this->milliseconds();
            // Order $signature expiry must be between 2592000 and 7776000 sec from now
            $signatureExpiry = $this->safe_integer($params, 'signature_expiry_sec', $this->seconds() + 7776000);
            $ACTION_TYPEHASH = $this->base16_to_binary('4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17');
            $sandboxMode = $this->safe_bool($this->options, 'sandboxMode', false);
            $TRADE_MODULE_ADDRESS = ($sandboxMode) ? '0x87F2863866D85E3192a35A73b388BD625D83f2be' : '0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b';
            $priceString = $this->number_to_string($price);
            $maxFee = null;
            list($maxFee, $params) = $this->handle_option_and_params($params, 'createOrder', 'max_fee');
            if ($maxFee === null) {
                throw new ArgumentsRequired($this->id . ' createOrder() requires a max_fee argument in params');
            }
            $maxFeeString = $this->number_to_string($maxFee);
            $amountString = $this->number_to_string($amount);
            $tradeModuleDataHash = $this->hash($this->eth_abi_encode(array(
                'address', 'uint', 'int', 'int', 'uint', 'uint', 'bool',
            ), [
                $market['info']['base_asset_address'],
                $this->parse_to_numeric($market['info']['base_asset_sub_id']),
                $this->convert_to_big_int($this->parse_units($priceString)),
                $this->convert_to_big_int($this->parse_units($this->amount_to_precision($symbol, $amountString))),
                $this->convert_to_big_int($this->parse_units($maxFeeString)),
                $subaccountId,
                $orderSide === 'buy',
            ]), 'keccak', 'binary');
            $deriveWalletAddress = null;
            list($deriveWalletAddress, $params) = $this->handle_derive_wallet_address('createOrder', $params);
            $signature = $this->sign_order(array(
                $ACTION_TYPEHASH,
                $subaccountId,
                $nonce,
                $TRADE_MODULE_ADDRESS,
                $tradeModuleDataHash,
                $signatureExpiry,
                $deriveWalletAddress,
                $this->walletAddress,
            ), $this->privateKey);
            $request = array(
                'instrument_name' => $market['id'],
                'direction' => $orderSide,
                'order_type' => $orderType,
                'nonce' => $nonce,
                'amount' => $amountString,
                'limit_price' => $priceString,
                'max_fee' => $maxFeeString,
                'subaccount_id' => $subaccountId,
                'signature_expiry_sec' => $signatureExpiry,
                'referral_code' => $this->safe_string($this->options, 'id', '0x0ad42b8e602c2d3d475ae52d678cf63d84ab2749'),
                'signer' => $this->walletAddress,
            );
            if ($reduceOnly !== null) {
                $request['reduce_only'] = $reduceOnly;
                if ($reduceOnly && $postOnly) {
                    throw new InvalidOrder($this->id . ' cannot use reduce only with post only time in force');
                }
            }
            if ($postOnly !== null) {
                $request['time_in_force'] = 'post_only';
            } elseif ($timeInForce !== null) {
                $request['time_in_force'] = $timeInForce;
            }
            $stopLoss = $this->safe_value($params, 'stopLoss');
            $takeProfit = $this->safe_value($params, 'takeProfit');
            $triggerPriceType = $this->safe_string($params, 'trigger_price_type', 'mark');
            if ($stopLoss !== null) {
                $stopLossPrice = $this->safe_string($stopLoss, 'triggerPrice', $stopLoss);
                $request['trigger_price'] = $stopLossPrice;
                $request['trigger_type'] = 'stoploss';
                $request['trigger_price_type'] = $triggerPriceType;
            } elseif ($takeProfit !== null) {
                $takeProfitPrice = $this->safe_string($takeProfit, 'triggerPrice', $takeProfit);
                $request['trigger_price'] = $takeProfitPrice;
                $request['trigger_type'] = 'takeprofit';
                $request['trigger_price_type'] = $triggerPriceType;
            }
            $clientOrderId = $this->safe_string($params, 'clientOrderId');
            if ($clientOrderId !== null) {
                $request['label'] = $clientOrderId;
            }
            $request['signature'] = $signature;
            $params = $this->omit($params, array( 'reduceOnly', 'reduce_only', 'timeInForce', 'time_in_force', 'postOnly', 'test', 'clientOrderId', 'stopPrice', 'triggerPrice', 'trigger_price', 'stopLoss', 'takeProfit', 'trigger_price_type' ));
            $response = null;
            if ($test) {
                $response = Async\await($this->privatePostOrderDebug ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privatePostOrder ($this->extend($request, $params)));
            }
            //
            // {
            //     "result" => {
            //         "raw_data" => array(
            //             "subaccount_id" => 130837,
            //             "nonce" => 1736923517552,
            //             "module" => "0x87F2863866D85E3192a35A73b388BD625D83f2be",
            //             "expiry" => 86400,
            //             "owner" => "0x108b9aF9279a525b8A8AeAbE7AC2bA925Bc50075",
            //             "signer" => "0x108b9aF9279a525b8A8AeAbE7AC2bA925Bc50075",
            //             "signature" => "0xaa4f42b2f3da33c668fa703ea872d4c3a6b55aca66025b5119e3bebb6679fe2e2794638db51dcace21fc39a498047835994f07eb59f311bb956ce057e66793d1c",
            //             "data" => array(
            //                 "asset" => "0xAFB6Bb95cd70D5367e2C39e9dbEb422B9815339D",
            //                 "sub_id" => 0,
            //                 "limit_price" => "10000",
            //                 "desired_amount" => "0.001",
            //                 "worst_fee" => "0",
            //                 "recipient_id" => 130837,
            //                 "is_bid" => true,
            //                 "trade_id" => ""
            //             }
            //         ),
            //         "encoded_data" => "0x000000000000000000000000afb6bb95cd70d5367e2c39e9dbeb422b9815339d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff150000000000000000000000000000000000000000000000000000000000000001",
            //         "encoded_data_hashed" => "0xe88fb416bc54dba2d288988f1a82fee40fd792ed555b3471b5f6b4b810d279b4",
            //         "action_hash" => "0x273a0befb3751fa991edc7ed73582456c3b50ae964d458c8f472e932fb6a0069",
            //         "typed_data_hash" => "0x123e2d2f3d5b2473b4e260f51c6459d6bf904e5db8f042a3ea63be8d55329ce9"
            //     ),
            //     "id" => "f851c8c4-dddf-4b77-93cf-aeddd0966f29"
            // }
            // {
            //     "result" => array(
            //         "order" => array(
            //             "subaccount_id" => 130837,
            //             "order_id" => "96349ebb-7d46-43ae-81c7-7ab390444293",
            //             "instrument_name" => "BTC-PERP",
            //             "direction" => "buy",
            //             "label" => "",
            //             "quote_id" => null,
            //             "creation_timestamp" => 1737467576257,
            //             "last_update_timestamp" => 1737467576257,
            //             "limit_price" => "10000",
            //             "amount" => "0.01",
            //             "filled_amount" => "0",
            //             "average_price" => "0",
            //             "order_fee" => "0",
            //             "order_type" => "limit",
            //             "time_in_force" => "gtc",
            //             "order_status" => "open",
            //             "max_fee" => "210",
            //             "signature_expiry_sec" => 1737468175989,
            //             "nonce" => 1737467575989,
            //             "signer" => "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
            //             "signature" => "0xd1ca49df1fa06bd805bb59b132ff6c0de29bf973a3e01705abe0a01cc956e4945ed9eb99ab68f3df4c037908113cac5a5bfc3a954a0b7103cdab285962fa6a51c",
            //             "cancel_reason" => "",
            //             "mmp" => false,
            //             "is_transfer" => false,
            //             "replaced_order_id" => null,
            //             "trigger_type" => null,
            //             "trigger_price_type" => null,
            //             "trigger_price" => null,
            //             "trigger_reject_message" => null
            //         ),
            //         "trades" => array()
            //     ),
            //     "id" => "397087fa-0125-42af-bfc3-f66166f9fb55"
            // }
            //
            $result = $this->safe_dict($response, 'result');
            $rawOrder = $this->safe_dict($result, 'raw_data');
            if ($rawOrder === null) {
                $rawOrder = $this->safe_dict($result, 'order');
            }
            $order = $this->parse_order($rawOrder, $market);
            $order['type'] = $type;
            return $order;
        }) ();
    }

    public function edit_order(string $id, string $symbol, string $type, string $side, ?float $amount = null, ?float $price = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $type, $side, $amount, $price, $params) {
            /**
             * edit a trade $order
             *
             * @see https://docs.derive.xyz/reference/post_private-replace
             *
             * @param {string} $id $order $id
             * @param {string} $symbol unified $symbol of the $market to create an $order in
             * @param {string} $type 'market' or 'limit'
             * @param {string} $side 'buy' or 'sell'
             * @param {float} $amount how much of currency you want to trade in units of base currency
             * @param {float} [$price] the $price at which the $order is to be fulfilled, in units of the quote currency, ignored in $market orders
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subaccount_id] *required* the subaccount $id
             * @return {array} an ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
             */
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('editOrder', $params);
            $reduceOnly = $this->safe_bool_2($params, 'reduceOnly', 'reduce_only');
            $timeInForce = $this->safe_string_lower_2($params, 'timeInForce', 'time_in_force');
            $postOnly = $this->safe_bool($params, 'postOnly');
            $orderType = strtolower($type);
            $orderSide = strtolower($side);
            $nonce = $this->milliseconds();
            $signatureExpiry = $this->safe_number($params, 'signature_expiry_sec', $this->seconds() + 7776000);
            // TODO => subaccount $id / trade module address
            $ACTION_TYPEHASH = $this->base16_to_binary('4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17');
            $sandboxMode = $this->safe_bool($this->options, 'sandboxMode', false);
            $TRADE_MODULE_ADDRESS = ($sandboxMode) ? '0x87F2863866D85E3192a35A73b388BD625D83f2be' : '0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b';
            $priceString = $this->number_to_string($price);
            $maxFeeString = $this->safe_string($params, 'max_fee', '0');
            $amountString = $this->number_to_string($amount);
            $tradeModuleDataHash = $this->hash($this->eth_abi_encode(array(
                'address', 'uint', 'int', 'int', 'uint', 'uint', 'bool',
            ), [
                $market['info']['base_asset_address'],
                $this->parse_to_numeric($market['info']['base_asset_sub_id']),
                $this->convert_to_big_int($this->parse_units($priceString)),
                $this->convert_to_big_int($this->parse_units($this->amount_to_precision($symbol, $amountString))),
                $this->convert_to_big_int($this->parse_units($maxFeeString)),
                $subaccountId,
                $orderSide === 'buy',
            ]), 'keccak', 'binary');
            $deriveWalletAddress = null;
            list($deriveWalletAddress, $params) = $this->handle_derive_wallet_address('editOrder', $params);
            $signature = $this->sign_order(array(
                $ACTION_TYPEHASH,
                $subaccountId,
                $nonce,
                $TRADE_MODULE_ADDRESS,
                $tradeModuleDataHash,
                $signatureExpiry,
                $deriveWalletAddress,
                $this->walletAddress,
            ), $this->privateKey);
            $request = array(
                'instrument_name' => $market['id'],
                'order_id_to_cancel' => $id,
                'direction' => $orderSide,
                'order_type' => $orderType,
                'nonce' => $nonce,
                'amount' => $amountString,
                'limit_price' => $priceString,
                'max_fee' => $maxFeeString,
                'subaccount_id' => $subaccountId,
                'signature_expiry_sec' => $signatureExpiry,
                'signer' => $this->walletAddress,
            );
            if ($reduceOnly !== null) {
                $request['reduce_only'] = $reduceOnly;
                if ($reduceOnly && $postOnly) {
                    throw new InvalidOrder($this->id . ' cannot use reduce only with post only time in force');
                }
            }
            if ($postOnly !== null) {
                $request['time_in_force'] = 'post_only';
            } elseif ($timeInForce !== null) {
                $request['time_in_force'] = $timeInForce;
            }
            $clientOrderId = $this->safe_string($params, 'clientOrderId');
            if ($clientOrderId !== null) {
                $request['label'] = $clientOrderId;
            }
            $request['signature'] = $signature;
            $params = $this->omit($params, array( 'reduceOnly', 'reduce_only', 'timeInForce', 'time_in_force', 'postOnly', 'clientOrderId' ));
            $response = Async\await($this->privatePostReplace ($this->extend($request, $params)));
            //
            //   {
            //     "result":
            //       array(
            //         "cancelled_order":
            //           array(
            //             "subaccount_id" => 130837,
            //             "order_id" => "c2337704-f1af-437d-91c8-dddb9d6bac59",
            //             "instrument_name" => "BTC-PERP",
            //             "direction" => "buy",
            //             "label" => "test1234",
            //             "quote_id" => null,
            //             "creation_timestamp" => 1737539743959,
            //             "last_update_timestamp" => 1737539764234,
            //             "limit_price" => "10000",
            //             "amount" => "0.01",
            //             "filled_amount" => "0",
            //             "average_price" => "0",
            //             "order_fee" => "0",
            //             "order_type" => "limit",
            //             "time_in_force" => "post_only",
            //             "order_status" => "cancelled",
            //             "max_fee" => "211",
            //             "signature_expiry_sec" => 1737540343631,
            //             "nonce" => 1737539743631,
            //             "signer" => "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
            //             "signature" => "0xdb669e18f407a3efa816b79c0dd3bac1c651d4dbf3caad4db67678ce9b81c76378d787a08143a30707eb0827ce4626640767c9f174358df1b90611bd6d1391711b",
            //             "cancel_reason" => "user_request",
            //             "mmp" => false,
            //             "is_transfer" => false,
            //             "replaced_order_id" => null,
            //             "trigger_type" => null,
            //             "trigger_price_type" => null,
            //             "trigger_price" => null,
            //             "trigger_reject_message" => null,
            //           ),
            //         "order":
            //           array(
            //             "subaccount_id" => 130837,
            //             "order_id" => "97af0902-813f-4892-a54b-797e5689db05",
            //             "instrument_name" => "BTC-PERP",
            //             "direction" => "buy",
            //             "label" => "test1234",
            //             "quote_id" => null,
            //             "creation_timestamp" => 1737539764154,
            //             "last_update_timestamp" => 1737539764154,
            //             "limit_price" => "10000",
            //             "amount" => "0.01",
            //             "filled_amount" => "0",
            //             "average_price" => "0",
            //             "order_fee" => "0",
            //             "order_type" => "limit",
            //             "time_in_force" => "post_only",
            //             "order_status" => "open",
            //             "max_fee" => "211",
            //             "signature_expiry_sec" => 1737540363890,
            //             "nonce" => 1737539763890,
            //             "signer" => "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
            //             "signature" => "0xef2c459ab4797cbbd7d97b47678ff172542af009bac912bf53e7879cf92eb1aa6b1a6cf40bf0928684f5394942fb424cc2db71eac0eaf7226a72480034332f291c",
            //             "cancel_reason" => "",
            //             "mmp" => false,
            //             "is_transfer" => false,
            //             "replaced_order_id" => "c2337704-f1af-437d-91c8-dddb9d6bac59",
            //             "trigger_type" => null,
            //             "trigger_price_type" => null,
            //             "trigger_price" => null,
            //             "trigger_reject_message" => null,
            //           ),
            //         "trades" => array(),
            //         "create_order_error" => null,
            //       ),
            //     "id" => "fb19e991-15f6-4c80-a20c-917e762a1a38",
            //   }
            //
            $result = $this->safe_dict($response, 'result');
            $rawOrder = $this->safe_dict($result, 'order');
            $order = $this->parse_order($rawOrder, $market);
            return $order;
        }) ();
    }

    public function cancel_order(string $id, ?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $params) {
            /**
             *
             * @see https://docs.derive.xyz/reference/post_private-cancel
             *
             * 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
             * @param {boolean} [$params->trigger] whether the $order is a trigger/algo $order
             * @param {string} [$params->subaccount_id] *required* the subaccount $id
             * @return {array} An ~@link https://docs.ccxt.com/#/?$id=$order-structure $order structure~
             */
            if ($symbol === null) {
                throw new ArgumentsRequired($this->id . ' cancelOrder() requires a $symbol argument');
            }
            Async\await($this->load_markets());
            $market = $this->market($symbol);
            $isTrigger = $this->safe_bool_2($params, 'trigger', 'stop', false);
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('cancelOrder', $params);
            $params = $this->omit($params, array( 'trigger', 'stop' ));
            $request = array(
                'instrument_name' => $market['id'],
                'subaccount_id' => $subaccountId,
            );
            $clientOrderIdUnified = $this->safe_string($params, 'clientOrderId');
            $clientOrderIdExchangeSpecific = $this->safe_string($params, 'label', $clientOrderIdUnified);
            $isByClientOrder = $clientOrderIdExchangeSpecific !== null;
            $response = null;
            if ($isByClientOrder) {
                $request['label'] = $clientOrderIdExchangeSpecific;
                $params = $this->omit($params, array( 'clientOrderId', 'label' ));
                $response = Async\await($this->privatePostCancelByLabel ($this->extend($request, $params)));
            } else {
                $request['order_id'] = $id;
                if ($isTrigger) {
                    $response = Async\await($this->privatePostCancelTriggerOrder ($this->extend($request, $params)));
                } else {
                    $response = Async\await($this->privatePostCancel ($this->extend($request, $params)));
                }
            }
            //
            // {
            //     "result" => array(
            //         "subaccount_id" => 130837,
            //         "order_id" => "de4f30b6-0dcb-4df6-9222-c1a27f1ad80d",
            //         "instrument_name" => "BTC-PERP",
            //         "direction" => "buy",
            //         "label" => "test1234",
            //         "quote_id" => null,
            //         "creation_timestamp" => 1737540100989,
            //         "last_update_timestamp" => 1737540574696,
            //         "limit_price" => "10000",
            //         "amount" => "0.01",
            //         "filled_amount" => "0",
            //         "average_price" => "0",
            //         "order_fee" => "0",
            //         "order_type" => "limit",
            //         "time_in_force" => "post_only",
            //         "order_status" => "cancelled",
            //         "max_fee" => "211",
            //         "signature_expiry_sec" => 1737540700726,
            //         "nonce" => 1737540100726,
            //         "signer" => "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
            //         "signature" => "0x9cd1a6e32a0699929e4e090c08c548366b1353701ec56e02d5cdf37fc89bd19b7b29e00e57e8383bb6336d73019027a7e2a4364f40859e7a949115024c7f199a1b",
            //         "cancel_reason" => "user_request",
            //         "mmp" => false,
            //         "is_transfer" => false,
            //         "replaced_order_id" => "4ccc89ba-3c3d-4047-8900-0aa5fb4ef706",
            //         "trigger_type" => null,
            //         "trigger_price_type" => null,
            //         "trigger_price" => null,
            //         "trigger_reject_message" => null
            //     ),
            //     "id" => "cef61e2a-cb13-4779-8e6b-535361981fad"
            // }
            //
            // {
            //     "result" => array(
            //         "cancelled_orders" => 1
            //     ),
            //     "id" => "674e075e-1e8a-4a47-99ff-75efbdd2370f"
            // }
            //
            $extendParams = array( 'symbol' => $symbol );
            $order = $this->safe_dict($response, 'result');
            if ($isByClientOrder) {
                $extendParams['client_order_id'] = $clientOrderIdExchangeSpecific;
            }
            return $this->extend($this->parse_order($order, $market), $extendParams);
        }) ();
    }

    public function cancel_all_orders(?string $symbol = null, $params = array ()) {
        return Async\async(function () use ($symbol, $params) {
            /**
             *
             * @see https://docs.derive.xyz/reference/post_private-cancel-by-instrument
             * @see https://docs.derive.xyz/reference/post_private-cancel-all
             *
             * cancel all open orders in a $market
             * @param {string} $symbol unified $market $symbol
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subaccount_id] *required* the subaccount id
             * @return {array} an list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
            }
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('cancelAllOrders', $params);
            $request = array(
                'subaccount_id' => $subaccountId,
            );
            $response = null;
            if ($market !== null) {
                $request['instrument_name'] = $market['id'];
                $response = Async\await($this->privatePostCancelByInstrument ($this->extend($request, $params)));
            } else {
                $response = Async\await($this->privatePostCancelAll ($this->extend($request, $params)));
            }
            //
            // {
            //     "result" => array(
            //         "cancelled_orders" => 0
            //     ),
            //     "id" => "9d633799-2098-4559-b547-605bb6f4d8f4"
            // }
            //
            // {
            //     "id" => "45548646-c74f-4ca2-9de4-551e6de49afa",
            //     "result" => "ok"
            // }
            //
            return array( $this->safe_order(array( 'info' => $response )) );
        }) ();
    }

    public function fetch_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple $orders made by the user
             *
             * @see https://docs.derive.xyz/reference/post_private-get-$orders
             *
             * @param {string} $symbol unified $market $symbol of the $market $orders were made in
             * @param {int} [$since] the earliest time in ms to fetch $orders for
             * @param {int} [$limit] the maximum number of order structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->paginate] set to true if you want to fetch $orders with $pagination
             * @param {boolean} [$params->trigger] whether the order is a trigger/algo order
             * @param {string} [$params->subaccount_id] *required* the subaccount id
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchOrders', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_incremental('fetchOrders', $symbol, $since, $limit, $params, 'page', 500));
            }
            $isTrigger = $this->safe_bool_2($params, 'trigger', 'stop', false);
            $params = $this->omit($params, array( 'trigger', 'stop' ));
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('fetchOrders', $params);
            $request = array(
                'subaccount_id' => $subaccountId,
            );
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['instrument_name'] = $market['id'];
            }
            if ($limit !== null) {
                $request['page_size'] = $limit;
            } else {
                $request['page_size'] = 500;
            }
            if ($isTrigger) {
                $request['status'] = 'untriggered';
            }
            $response = Async\await($this->privatePostGetOrders ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "subaccount_id" => 130837,
            //         "orders" => array(
            //             {
            //                 "subaccount_id" => 130837,
            //                 "order_id" => "63a80cb8-387b-472b-a838-71cd9513c365",
            //                 "instrument_name" => "BTC-PERP",
            //                 "direction" => "buy",
            //                 "label" => "test1234",
            //                 "quote_id" => null,
            //                 "creation_timestamp" => 1737551053207,
            //                 "last_update_timestamp" => 1737551053207,
            //                 "limit_price" => "10000",
            //                 "amount" => "0.01",
            //                 "filled_amount" => "0",
            //                 "average_price" => "0",
            //                 "order_fee" => "0",
            //                 "order_type" => "limit",
            //                 "time_in_force" => "post_only",
            //                 "order_status" => "open",
            //                 "max_fee" => "211",
            //                 "signature_expiry_sec" => 1737551652765,
            //                 "nonce" => 1737551052765,
            //                 "signer" => "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
            //                 "signature" => "0x35535ccb1bcad509ecc435c79e966174db6403fc9aeee1e237d08a941014c57b59279dfe4be39e081f9921a53eaad59cb2a151d9f52f2d05fc47e6280254952e1c",
            //                 "cancel_reason" => "",
            //                 "mmp" => false,
            //                 "is_transfer" => false,
            //                 "replaced_order_id" => null,
            //                 "trigger_type" => null,
            //                 "trigger_price_type" => null,
            //                 "trigger_price" => null,
            //                 "trigger_reject_message" => null
            //             }
            //         ),
            //         "pagination" => array(
            //             "num_pages" => 1,
            //             "count" => 1
            //         }
            //     ),
            //     "id" => "e5a88d4f-7ac7-40cd-aec9-e0e8152b8b92"
            // }
            //
            $data = $this->safe_value($response, 'result');
            $page = $this->safe_integer($params, 'page');
            if ($page !== null) {
                $pagination = $this->safe_dict($data, 'pagination');
                $currentPage = $this->safe_integer($pagination, 'num_pages');
                if ($page > $currentPage) {
                    return array();
                }
            }
            $orders = $this->safe_list($data, 'orders');
            return $this->parse_orders($orders, $market, $since, $limit);
        }) ();
    }

    public function fetch_open_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple orders made by the user
             *
             * @see https://docs.derive.xyz/reference/post_private-get-orders
             *
             * @param {string} $symbol unified market $symbol of the market orders were made in
             * @param {int} [$since] the earliest time in ms to fetch orders for
             * @param {int} [$limit] the maximum number of order structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->paginate] set to true if you want to fetch orders with pagination
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $extendedParams = $this->extend($params, array( 'status' => 'open' ));
            return Async\await($this->fetch_orders($symbol, $since, $limit, $extendedParams));
        }) ();
    }

    public function fetch_closed_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple orders made by the user
             *
             * @see https://docs.derive.xyz/reference/post_private-get-orders
             *
             * @param {string} $symbol unified market $symbol of the market orders were made in
             * @param {int} [$since] the earliest time in ms to fetch orders for
             * @param {int} [$limit] the maximum number of order structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->paginate] set to true if you want to fetch orders with pagination
             * @return {Order[]} a list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $extendedParams = $this->extend($params, array( 'status' => 'filled' ));
            return Async\await($this->fetch_orders($symbol, $since, $limit, $extendedParams));
        }) ();
    }

    public function fetch_canceled_orders(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetches information on multiple canceled orders made by the user
             *
             * @see https://docs.derive.xyz/reference/post_private-get-orders
             *
             * @param {string} $symbol unified market $symbol of the market the orders were made in
             * @param {int} [$since] the earliest time in ms to fetch orders for
             * @param {int} [$limit] the maximum number of order structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {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 list of ~@link https://docs.ccxt.com/#/?id=order-structure order structures~
             */
            Async\await($this->load_markets());
            $extendedParams = $this->extend($params, array( 'status' => 'cancelled' ));
            return Async\await($this->fetch_orders($symbol, $since, $limit, $extendedParams));
        }) ();
    }

    public function parse_time_in_force(?string $timeInForce) {
        $timeInForces = array(
            'ioc' => 'IOC',
            'fok' => 'FOK',
            'gtc' => 'GTC',
            'post_only' => 'PO',
        );
        return $this->safe_string($timeInForces, $timeInForce, null);
    }

    public function parse_order_status(?string $status) {
        if ($status !== null) {
            $statuses = array(
                'open' => 'open',
                'untriggered' => 'open',
                'filled' => 'closed',
                'cancelled' => 'canceled',
                'expired' => 'rejected',
            );
            return $this->safe_string($statuses, $status, $status);
        }
        return $status;
    }

    public function parse_order(array $rawOrder, ?array $market = null): array {
        //
        // {
        //     "subaccount_id" => 130837,
        //     "nonce" => 1736923517552,
        //     "module" => "0x87F2863866D85E3192a35A73b388BD625D83f2be",
        //     "expiry" => 86400,
        //     "owner" => "0x108b9aF9279a525b8A8AeAbE7AC2bA925Bc50075",
        //     "signer" => "0x108b9aF9279a525b8A8AeAbE7AC2bA925Bc50075",
        //     "signature" => "0xaa4f42b2f3da33c668fa703ea872d4c3a6b55aca66025b5119e3bebb6679fe2e2794638db51dcace21fc39a498047835994f07eb59f311bb956ce057e66793d1c",
        //     "data" => {
        //         "asset" => "0xAFB6Bb95cd70D5367e2C39e9dbEb422B9815339D",
        //         "sub_id" => 0,
        //         "limit_price" => "10000",
        //         "desired_amount" => "0.001",
        //         "worst_fee" => "0",
        //         "recipient_id" => 130837,
        //         "is_bid" => true,
        //         "trade_id" => ""
        //     }
        // }
        // {
        //     "subaccount_id" => 130837,
        //     "order_id" => "96349ebb-7d46-43ae-81c7-7ab390444293",
        //     "instrument_name" => "BTC-PERP",
        //     "direction" => "buy",
        //     "label" => "",
        //     "quote_id" => null,
        //     "creation_timestamp" => 1737467576257,
        //     "last_update_timestamp" => 1737467576257,
        //     "limit_price" => "10000",
        //     "amount" => "0.01",
        //     "filled_amount" => "0",
        //     "average_price" => "0",
        //     "order_fee" => "0",
        //     "order_type" => "limit",
        //     "time_in_force" => "gtc",
        //     "order_status" => "open",
        //     "max_fee" => "210",
        //     "signature_expiry_sec" => 1737468175989,
        //     "nonce" => 1737467575989,
        //     "signer" => "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
        //     "signature" => "0xd1ca49df1fa06bd805bb59b132ff6c0de29bf973a3e01705abe0a01cc956e4945ed9eb99ab68f3df4c037908113cac5a5bfc3a954a0b7103cdab285962fa6a51c",
        //     "cancel_reason" => "",
        //     "mmp" => false,
        //     "is_transfer" => false,
        //     "replaced_order_id" => null,
        //     "trigger_type" => null,
        //     "trigger_price_type" => null,
        //     "trigger_price" => null,
        //     "trigger_reject_message" => null
        // }
        $order = $this->safe_dict($rawOrder, 'data');
        if ($order === null) {
            $order = $rawOrder;
        }
        $timestamp = $this->safe_integer_2($rawOrder, 'creation_timestamp', 'nonce');
        $orderId = $this->safe_string($order, 'order_id');
        $marketId = $this->safe_string($order, 'instrument_name');
        if ($marketId !== null) {
            $market = $this->safe_market($marketId, $market);
        }
        $symbol = $market['symbol'];
        $price = $this->safe_string($order, 'limit_price');
        $average = $this->safe_string($order, 'average_price');
        $amount = $this->safe_string($order, 'desired_amount');
        $filled = $this->safe_string($order, 'filled_amount');
        $fee = $this->safe_string($order, 'order_fee');
        $orderType = $this->safe_string_lower($order, 'order_type');
        $isBid = $this->safe_bool($order, 'is_bid');
        $side = $this->safe_string($order, 'direction');
        if ($side === null) {
            if ($isBid) {
                $side = 'buy';
            } else {
                $side = 'sell';
            }
        }
        $triggerType = $this->safe_string($order, 'trigger_type');
        $stopLossPrice = null;
        $takeProfitPrice = null;
        $triggerPrice = null;
        if ($triggerType !== null) {
            $triggerPrice = $this->safe_string($order, 'trigger_price');
            if ($triggerType === 'stoploss') {
                $stopLossPrice = $triggerPrice;
            } else {
                $takeProfitPrice = $triggerPrice;
            }
        }
        $lastUpdateTimestamp = $this->safe_integer($rawOrder, 'last_update_timestamp');
        $status = $this->safe_string($order, 'order_status');
        $timeInForce = $this->safe_string($order, 'time_in_force');
        return $this->safe_order(array(
            'id' => $orderId,
            'clientOrderId' => $this->safe_string($order, 'label'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastTradeTimestamp' => null,
            'lastUpdateTimestamp' => $lastUpdateTimestamp,
            'status' => $this->parse_order_status($status),
            'symbol' => $symbol,
            'type' => $orderType,
            'timeInForce' => $this->parse_time_in_force($timeInForce),
            'postOnly' => null, // handled in safeOrder
            'reduceOnly' => $this->safe_bool($order, 'reduce_only'),
            'side' => $side,
            'price' => $price,
            'triggerPrice' => $triggerPrice,
            'takeProfitPrice' => $takeProfitPrice,
            'stopLossPrice' => $stopLossPrice,
            'average' => $average,
            'amount' => $amount,
            'filled' => $filled,
            'remaining' => null,
            'cost' => null,
            'trades' => null,
            'fee' => array(
                'cost' => $fee,
                'currency' => 'USDC',
            ),
            'info' => $order,
        ), $market);
    }

    public function fetch_order_trades(string $id, ?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($id, $symbol, $since, $limit, $params) {
            /**
             * fetch all the $trades made from a single order
             *
             * @see https://docs.derive.xyz/reference/post_private-get-trade-history
             *
             * @param {string} $id order $id
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch $trades for
             * @param {int} [$limit] the maximum number of $trades to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subaccount_id] *required* the subaccount $id
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?$id=trade-structure trade structures~
             */
            Async\await($this->load_markets());
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('fetchOrderTrades', $params);
            $request = array(
                'order_id' => $id,
                'subaccount_id' => $subaccountId,
            );
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['instrument_name'] = $market['id'];
            }
            if ($limit !== null) {
                $request['page_size'] = $limit;
            }
            if ($since !== null) {
                $request['from_timestamp'] = $since;
            }
            $response = Async\await($this->privatePostGetTradeHistory ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "subaccount_id" => 130837,
            //         "trades" => array(
            //             {
            //                 "subaccount_id" => 130837,
            //                 "order_id" => "30c48194-8d48-43ac-ad00-0d5ba29eddc9",
            //                 "instrument_name" => "BTC-PERP",
            //                 "direction" => "sell",
            //                 "label" => "test1234",
            //                 "quote_id" => null,
            //                 "trade_id" => "f8a30740-488c-4c2d-905d-e17057bafde1",
            //                 "timestamp" => 1738065303708,
            //                 "mark_price" => "102740.137375457314192317",
            //                 "index_price" => "102741.553409299981533184",
            //                 "trade_price" => "102700.6",
            //                 "trade_amount" => "0.01",
            //                 "liquidity_role" => "taker",
            //                 "realized_pnl" => "0",
            //                 "realized_pnl_excl_fees" => "0",
            //                 "is_transfer" => false,
            //                 "tx_status" => "settled",
            //                 "trade_fee" => "1.127415534092999815",
            //                 "tx_hash" => "0xc55df1f07330faf86579bd8a6385391fbe9e73089301149d8550e9d29c9ead74",
            //                 "transaction_id" => "e18b9426-3fa5-41bb-99d3-8b54fb4d51bb"
            //             }
            //         ),
            //         "pagination" => array(
            //             "num_pages" => 1,
            //             "count" => 1
            //         }
            //     ),
            //     "id" => "a16f798c-a121-44e2-b77e-c38a063f8a99"
            // }
            //
            $result = $this->safe_dict($response, 'result', array());
            $trades = $this->safe_list($result, 'trades', array());
            return $this->parse_trades($trades, $market, $since, $limit, $params);
        }) ();
    }

    public function fetch_my_trades(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch all $trades made by the user
             *
             * @see https://docs.derive.xyz/reference/post_private-get-trade-history
             *
             * @param {string} $symbol unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch $trades for
             * @param {int} [$limit] the maximum number of $trades structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->paginate] set to true if you want to fetch $trades with $pagination
             * @param {string} [$params->subaccount_id] *required* the subaccount id
             * @return {Trade[]} a list of ~@link https://docs.ccxt.com/#/?id=trade-structure trade structures~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchMyTrades', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_incremental('fetchMyTrades', $symbol, $since, $limit, $params, 'page', 500));
            }
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('fetchMyTrades', $params);
            $request = array(
                'subaccount_id' => $subaccountId,
            );
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['instrument_name'] = $market['id'];
            }
            if ($limit !== null) {
                $request['page_size'] = $limit;
            }
            if ($since !== null) {
                $request['from_timestamp'] = $since;
            }
            $response = Async\await($this->privatePostGetTradeHistory ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "subaccount_id" => 130837,
            //         "trades" => array(
            //             {
            //                 "subaccount_id" => 130837,
            //                 "order_id" => "30c48194-8d48-43ac-ad00-0d5ba29eddc9",
            //                 "instrument_name" => "BTC-PERP",
            //                 "direction" => "sell",
            //                 "label" => "test1234",
            //                 "quote_id" => null,
            //                 "trade_id" => "f8a30740-488c-4c2d-905d-e17057bafde1",
            //                 "timestamp" => 1738065303708,
            //                 "mark_price" => "102740.137375457314192317",
            //                 "index_price" => "102741.553409299981533184",
            //                 "trade_price" => "102700.6",
            //                 "trade_amount" => "0.01",
            //                 "liquidity_role" => "taker",
            //                 "realized_pnl" => "0",
            //                 "realized_pnl_excl_fees" => "0",
            //                 "is_transfer" => false,
            //                 "tx_status" => "settled",
            //                 "trade_fee" => "1.127415534092999815",
            //                 "tx_hash" => "0xc55df1f07330faf86579bd8a6385391fbe9e73089301149d8550e9d29c9ead74",
            //                 "transaction_id" => "e18b9426-3fa5-41bb-99d3-8b54fb4d51bb"
            //             }
            //         ),
            //         "pagination" => array(
            //             "num_pages" => 1,
            //             "count" => 1
            //         }
            //     ),
            //     "id" => "a16f798c-a121-44e2-b77e-c38a063f8a99"
            // }
            //
            $result = $this->safe_dict($response, 'result', array());
            $page = $this->safe_integer($params, 'page');
            if ($page !== null) {
                $pagination = $this->safe_dict($result, 'pagination');
                $currentPage = $this->safe_integer($pagination, 'num_pages');
                if ($page > $currentPage) {
                    return array();
                }
            }
            $trades = $this->safe_list($result, 'trades', array());
            return $this->parse_trades($trades, $market, $since, $limit, $params);
        }) ();
    }

    public function fetch_positions(?array $symbols = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($symbols, $params) {
            /**
             * fetch all open $positions
             *
             * @see https://docs.derive.xyz/reference/post_private-get-$positions
             *
             * @param {string[]} [$symbols] not used by kraken fetchPositions ()
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subaccount_id] *required* the subaccount id
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=position-structure position structure~
             */
            Async\await($this->load_markets());
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('fetchPositions', $params);
            $request = array(
                'subaccount_id' => $subaccountId,
            );
            $params = $this->omit($params, array( 'subaccount_id' ));
            $response = Async\await($this->privatePostGetPositions ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "subaccount_id" => 130837,
            //         "positions" => array(
            //             array(
            //                 "instrument_type" => "perp",
            //                 "instrument_name" => "BTC-PERP",
            //                 "amount" => "-0.02",
            //                 "average_price" => "102632.9105389869500088",
            //                 "realized_pnl" => "0",
            //                 "unrealized_pnl" => "-2.6455959784245548835819950103759765625",
            //                 "total_fees" => "2.255789220260999824",
            //                 "average_price_excl_fees" => "102745.7",
            //                 "realized_pnl_excl_fees" => "0",
            //                 "unrealized_pnl_excl_fees" => "-0.3898067581635550595819950103759765625",
            //                 "net_settlements" => "-4.032902047219498639",
            //                 "cumulative_funding" => "-0.004677736347850093",
            //                 "pending_funding" => "0",
            //                 "mark_price" => "102765.190337908177752979099750518798828125",
            //                 "index_price" => "102767.657193800017641472",
            //                 "delta" => "1",
            //                 "gamma" => "0",
            //                 "vega" => "0",
            //                 "theta" => "0",
            //                 "mark_value" => "1.38730606879471451975405216217041015625",
            //                 "maintenance_margin" => "-101.37788426911356509663164615631103515625",
            //                 "initial_margin" => "-132.2074413704858670826070010662078857421875",
            //                 "open_orders_margin" => "264.116085900726830004714429378509521484375",
            //                 "leverage" => "8.6954476205089299495699106539379941746377322586618",
            //                 "liquidation_price" => "109125.705451984322280623018741607666015625",
            //                 "creation_timestamp" => 1738065303840
            //             }
            //         )
            //     ),
            //     "id" => "167350f1-d9fc-41d4-9797-1c78f83fda8e"
            // }
            //
            $result = $this->safe_dict($response, 'result', array());
            $positions = $this->safe_list($result, 'positions', array());
            return $this->parse_positions($positions, $symbols);
        }) ();
    }

    public function parse_position(array $position, ?array $market = null) {
        //
        // {
        //     "instrument_type" => "perp",
        //     "instrument_name" => "BTC-PERP",
        //     "amount" => "-0.02",
        //     "average_price" => "102632.9105389869500088",
        //     "realized_pnl" => "0",
        //     "unrealized_pnl" => "-2.6455959784245548835819950103759765625",
        //     "total_fees" => "2.255789220260999824",
        //     "average_price_excl_fees" => "102745.7",
        //     "realized_pnl_excl_fees" => "0",
        //     "unrealized_pnl_excl_fees" => "-0.3898067581635550595819950103759765625",
        //     "net_settlements" => "-4.032902047219498639",
        //     "cumulative_funding" => "-0.004677736347850093",
        //     "pending_funding" => "0",
        //     "mark_price" => "102765.190337908177752979099750518798828125",
        //     "index_price" => "102767.657193800017641472",
        //     "delta" => "1",
        //     "gamma" => "0",
        //     "vega" => "0",
        //     "theta" => "0",
        //     "mark_value" => "1.38730606879471451975405216217041015625",
        //     "maintenance_margin" => "-101.37788426911356509663164615631103515625",
        //     "initial_margin" => "-132.2074413704858670826070010662078857421875",
        //     "open_orders_margin" => "264.116085900726830004714429378509521484375",
        //     "leverage" => "8.6954476205089299495699106539379941746377322586618",
        //     "liquidation_price" => "109125.705451984322280623018741607666015625",
        //     "creation_timestamp" => 1738065303840
        // }
        //
        $contract = $this->safe_string($position, 'instrument_name');
        $market = $this->safe_market($contract, $market);
        $size = $this->safe_string($position, 'amount');
        $side = null;
        if (Precise::string_gt($size, '0')) {
            $side = 'long';
        } else {
            $side = 'short';
        }
        $contractSize = $this->safe_string($market, 'contractSize');
        $markPrice = $this->safe_string($position, 'mark_price');
        $timestamp = $this->safe_integer($position, 'creation_timestamp');
        $unrealisedPnl = $this->safe_string($position, 'unrealized_pnl');
        $size = Precise::string_abs($size);
        $notional = Precise::string_mul($size, $markPrice);
        return $this->safe_position(array(
            'info' => $position,
            'id' => null,
            'symbol' => $this->safe_string($market, 'symbol'),
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'lastUpdateTimestamp' => null,
            'initialMargin' => $this->safe_string($position, 'initial_margin'),
            'initialMarginPercentage' => null,
            'maintenanceMargin' => $this->safe_string($position, 'maintenance_margin'),
            'maintenanceMarginPercentage' => null,
            'entryPrice' => null,
            'notional' => $this->parse_number($notional),
            'leverage' => $this->safe_number($position, 'leverage'),
            'unrealizedPnl' => $this->parse_number($unrealisedPnl),
            'contracts' => $this->parse_number($size),
            'contractSize' => $this->parse_number($contractSize),
            'marginRatio' => null,
            'liquidationPrice' => $this->safe_number($position, 'liquidation_price'),
            'markPrice' => $this->parse_number($markPrice),
            'lastPrice' => null,
            'collateral' => null,
            'marginMode' => null,
            'side' => $side,
            'percentage' => null,
            'hedged' => null,
            'stopLossPrice' => null,
            'takeProfitPrice' => null,
        ));
    }

    public function fetch_funding_history(?string $symbol = null, ?int $since = null, ?int $limit = null, $params = array ()) {
        return Async\async(function () use ($symbol, $since, $limit, $params) {
            /**
             * fetch the history of funding payments paid and received on this account
             *
             * @see https://docs.derive.xyz/reference/post_private-get-funding-history
             *
             * @param {string} [$symbol] unified $market $symbol
             * @param {int} [$since] the earliest time in ms to fetch funding history for
             * @param {int} [$limit] the maximum number of funding history structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {boolean} [$params->paginate] default false, when true will automatically $paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#$pagination-$params)
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=funding-history-structure funding history structure~
             */
            Async\await($this->load_markets());
            $paginate = false;
            list($paginate, $params) = $this->handle_option_and_params($params, 'fetchFundingHistory', 'paginate');
            if ($paginate) {
                return Async\await($this->fetch_paginated_call_incremental('fetchFundingHistory', $symbol, $since, $limit, $params, 'page', 500));
            }
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('fetchFundingHistory', $params);
            $request = array(
                'subaccount_id' => $subaccountId,
            );
            $market = null;
            if ($symbol !== null) {
                $market = $this->market($symbol);
                $request['instrument_name'] = $market['id'];
            }
            if ($since !== null) {
                $request['start_timestamp'] = $since;
            }
            if ($limit !== null) {
                $request['page_size'] = $limit;
            }
            $response = Async\await($this->privatePostGetFundingHistory ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "events" => array(
            //             array(
            //                 "instrument_name" => "BTC-PERP",
            //                 "timestamp" => 1738066618272,
            //                 "funding" => "-0.004677736347850093",
            //                 "pnl" => "-0.944081615774632967"
            //             ),
            //             array(
            //                 "instrument_name" => "BTC-PERP",
            //                 "timestamp" => 1738066617964,
            //                 "funding" => "0",
            //                 "pnl" => "-0.437556413479249408"
            //             ),
            //             {
            //                 "instrument_name" => "BTC-PERP",
            //                 "timestamp" => 1738065307565,
            //                 "funding" => "0",
            //                 "pnl" => "-0.39547479770461644"
            //             }
            //         ),
            //         "pagination" => array(
            //             "num_pages" => 1,
            //             "count" => 3
            //         }
            //     ),
            //     "id" => "524b817f-2108-467f-8795-511066f4acec"
            // }
            //
            $result = $this->safe_dict($response, 'result', array());
            $page = $this->safe_integer($params, 'page');
            if ($page !== null) {
                $pagination = $this->safe_dict($result, 'pagination');
                $currentPage = $this->safe_integer($pagination, 'num_pages');
                if ($page > $currentPage) {
                    return array();
                }
            }
            $events = $this->safe_list($result, 'events', array());
            return $this->parse_incomes($events, $market, $since, $limit);
        }) ();
    }

    public function parse_income($income, ?array $market = null) {
        //
        // {
        //     "instrument_name" => "BTC-PERP",
        //     "timestamp" => 1738065307565,
        //     "funding" => "0",
        //     "pnl" => "-0.39547479770461644"
        // }
        //
        $marketId = $this->safe_string($income, 'instrument_name');
        $symbol = $this->safe_symbol($marketId, $market);
        $rate = $this->safe_string($income, 'funding');
        $code = $this->safe_currency_code('USDC');
        $timestamp = $this->safe_integer($income, 'timestamp');
        return array(
            'info' => $income,
            'symbol' => $symbol,
            'code' => $code,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'id' => null,
            'amount' => null,
            'rate' => $rate,
        );
    }

    public function fetch_balance($params = array ()): PromiseInterface {
        return Async\async(function () use ($params) {
            /**
             * query for balance and get the amount of funds available for trading or funds locked in orders
             *
             * @see https://docs.derive.xyz/reference/post_private-get-all-portfolios
             *
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @return {array} a ~@link https://docs.ccxt.com/#/?id=balance-structure balance structure~
             */
            Async\await($this->load_markets());
            $deriveWalletAddress = null;
            list($deriveWalletAddress, $params) = $this->handle_derive_wallet_address('fetchBalance', $params);
            $request = array(
                'wallet' => $deriveWalletAddress,
            );
            $response = Async\await($this->privatePostGetAllPortfolios ($this->extend($request, $params)));
            //
            // {
            //     "result" => [{
            //             "subaccount_id" => 130837,
            //             "label" => "",
            //             "currency" => "all",
            //             "margin_type" => "SM",
            //             "is_under_liquidation" => false,
            //             "positions_value" => "0",
            //             "collaterals_value" => "318.0760325000001103035174310207366943359375",
            //             "subaccount_value" => "318.0760325000001103035174310207366943359375",
            //             "positions_maintenance_margin" => "0",
            //             "positions_initial_margin" => "0",
            //             "collaterals_maintenance_margin" => "238.557024375000082727638073265552520751953125",
            //             "collaterals_initial_margin" => "190.845619500000083235136116854846477508544921875",
            //             "maintenance_margin" => "238.557024375000082727638073265552520751953125",
            //             "initial_margin" => "190.845619500000083235136116854846477508544921875",
            //             "open_orders_margin" => "0",
            //             "projected_margin_change" => "0",
            //             "open_orders" => array(),
            //             "positions" => array(),
            //             "collaterals" => array(
            //                 array(
            //                     "asset_type" => "erc20",
            //                     "asset_name" => "ETH",
            //                     "currency" => "ETH",
            //                     "amount" => "0.1",
            //                     "mark_price" => "3180.760325000000438272",
            //                     "mark_value" => "318.0760325000001103035174310207366943359375",
            //                     "cumulative_interest" => "0",
            //                     "pending_interest" => "0",
            //                     "initial_margin" => "190.845619500000083235136116854846477508544921875",
            //                     "maintenance_margin" => "238.557024375000082727638073265552520751953125",
            //                     "realized_pnl" => "0",
            //                     "average_price" => "3184.891931",
            //                     "unrealized_pnl" => "-0.413161",
            //                     "total_fees" => "0",
            //                     "average_price_excl_fees" => "3184.891931",
            //                     "realized_pnl_excl_fees" => "0",
            //                     "unrealized_pnl_excl_fees" => "-0.413161",
            //                     "open_orders_margin" => "0",
            //                     "creation_timestamp" => 1736860533493
            //                 }
            //             )
            //     )],
            //     "id" => "27b9a64e-3379-4ce6-a126-9fb941c4a970"
            // }
            //
            $result = $this->safe_list($response, 'result');
            return $this->parse_balance($result);
        }) ();
    }

    public function parse_balance($response): array {
        $result = array(
            'info' => $response,
        );
        for ($i = 0; $i < count($response); $i++) {
            $subaccount = $response[$i];
            $collaterals = $this->safe_list($subaccount, 'collaterals', array());
            for ($j = 0; $j < count($collaterals); $j++) {
                $balance = $collaterals[$j];
                $code = $this->safe_currency_code($this->safe_string($balance, 'currency'));
                $account = $this->safe_dict($result, $code);
                if ($account === null) {
                    $account = $this->account();
                    $account['total'] = $this->safe_string($balance, 'amount');
                } else {
                    $amount = $this->safe_string($balance, 'amount');
                    $account['total'] = Precise::string_add($account['total'], $amount);
                }
                $result[$code] = $account;
            }
        }
        return $this->safe_balance($result);
    }

    public function fetch_deposits(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all deposits made to an account
             *
             * @see https://docs.derive.xyz/reference/post_private-get-deposit-history
             *
             * @param {string} $code unified $currency $code
             * @param {int} [$since] the earliest time in ms to fetch deposits for
             * @param {int} [$limit] the maximum number of deposits structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subaccount_id] *required* the subaccount id
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
             */
            Async\await($this->load_markets());
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('fetchDeposits', $params);
            $request = array(
                'subaccount_id' => $subaccountId,
            );
            if ($since !== null) {
                $request['start_timestamp'] = $since;
            }
            $response = Async\await($this->privatePostGetDepositHistory ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "events" => array(
            //             array(
            //                 "timestamp" => 1736860533599,
            //                 "transaction_id" => "f2069395-ec00-49f5-925a-87202a5d240f",
            //                 "asset" => "ETH",
            //                 "amount" => "0.1",
            //                 "tx_status" => "settled",
            //                 "tx_hash" => "0xeda21a315c59302a19c42049b4cef05a10b685302b6cc3edbaf49102d91166d4",
            //                 "error_log" => array()
            //             }
            //         )
            //     ),
            //     "id" => "ceebc730-22ab-40cd-9941-33ceb2a74389"
            // }
            //
            $currency = $this->safe_currency($code);
            $result = $this->safe_dict($response, 'result', array());
            $events = $this->safe_list($result, 'events');
            return $this->parse_transactions($events, $currency, $since, $limit, $params);
        }) ();
    }

    public function fetch_withdrawals(?string $code = null, ?int $since = null, ?int $limit = null, $params = array ()): PromiseInterface {
        return Async\async(function () use ($code, $since, $limit, $params) {
            /**
             * fetch all withdrawals made from an account
             *
             * @see https://docs.derive.xyz/reference/post_private-get-withdrawal-history
             *
             * @param {string} $code unified $currency $code
             * @param {int} [$since] the earliest time in ms to fetch withdrawals for
             * @param {int} [$limit] the maximum number of withdrawals structures to retrieve
             * @param {array} [$params] extra parameters specific to the exchange API endpoint
             * @param {string} [$params->subaccount_id] *required* the subaccount id
             * @return {array[]} a list of ~@link https://docs.ccxt.com/#/?id=transaction-structure transaction structures~
             */
            Async\await($this->load_markets());
            $subaccountId = null;
            list($subaccountId, $params) = $this->handle_derive_subaccount_id('fetchWithdrawals', $params);
            $request = array(
                'subaccount_id' => $subaccountId,
            );
            if ($since !== null) {
                $request['start_timestamp'] = $since;
            }
            $response = Async\await($this->privatePostGetWithdrawalHistory ($this->extend($request, $params)));
            //
            // {
            //     "result" => {
            //         "events" => array(
            //             array(
            //                 "timestamp" => 1736860533599,
            //                 "transaction_id" => "f2069395-ec00-49f5-925a-87202a5d240f",
            //                 "asset" => "ETH",
            //                 "amount" => "0.1",
            //                 "tx_status" => "settled",
            //                 "tx_hash" => "0xeda21a315c59302a19c42049b4cef05a10b685302b6cc3edbaf49102d91166d4",
            //                 "error_log" => array()
            //             }
            //         )
            //     ),
            //     "id" => "ceebc730-22ab-40cd-9941-33ceb2a74389"
            // }
            //
            $currency = $this->safe_currency($code);
            $result = $this->safe_dict($response, 'result', array());
            $events = $this->safe_list($result, 'events');
            return $this->parse_transactions($events, $currency, $since, $limit, $params);
        }) ();
    }

    public function parse_transaction(array $transaction, ?array $currency = null): array {
        //
        // {
        //     "timestamp" => 1736860533599,
        //     "transaction_id" => "f2069395-ec00-49f5-925a-87202a5d240f",
        //     "asset" => "ETH",
        //     "amount" => "0.1",
        //     "tx_status" => "settled",
        //     "tx_hash" => "0xeda21a315c59302a19c42049b4cef05a10b685302b6cc3edbaf49102d91166d4",
        //     "error_log" => array()
        // }
        //
        $code = $this->safe_string($transaction, 'asset');
        $timestamp = $this->safe_integer($transaction, 'timestamp');
        $txId = $this->safe_string($transaction, 'tx_hash');
        if ($txId === '0x0') {
            $txId = null;
        }
        return array(
            'info' => $transaction,
            'id' => null,
            'txid' => $txId,
            'timestamp' => $timestamp,
            'datetime' => $this->iso8601($timestamp),
            'address' => null,
            'addressFrom' => null,
            'addressTo' => null,
            'tag' => null,
            'tagFrom' => null,
            'tagTo' => null,
            'type' => null,
            'amount' => $this->safe_number($transaction, 'amount'),
            'currency' => $code,
            'status' => $this->parse_transaction_status($this->safe_string($transaction, 'tx_status')),
            'updated' => null,
            'comment' => null,
            'internal' => null,
            'fee' => null,
            'network' => null,
        );
    }

    public function parse_transaction_status(?string $status) {
        $statuses = array(
            'settled' => 'ok',
            'reverted' => 'failed',
        );
        return $this->safe_string($statuses, $status, $status);
    }

    public function handle_derive_subaccount_id(string $methodName, array $params) {
        $derivesubAccountId = null;
        list($derivesubAccountId, $params) = $this->handle_option_and_params($params, $methodName, 'subaccount_id');
        if (($derivesubAccountId !== null) && ($derivesubAccountId !== '')) {
            $this->options['subaccount_id'] = $derivesubAccountId; // saving in options
            return array( $derivesubAccountId, $params );
        }
        $optionsWallet = $this->safe_string($this->options, 'subaccount_id');
        if ($optionsWallet !== null) {
            return array( $optionsWallet, $params );
        }
        throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires a subaccount_id parameter inside \'params\' or exchange.options[\'subaccount_id\']=ID.');
    }

    public function handle_derive_wallet_address(string $methodName, array $params) {
        $deriveWalletAddress = null;
        list($deriveWalletAddress, $params) = $this->handle_option_and_params($params, $methodName, 'deriveWalletAddress');
        if (($deriveWalletAddress !== null) && ($deriveWalletAddress !== '')) {
            $this->options['deriveWalletAddress'] = $deriveWalletAddress; // saving in options
            return array( $deriveWalletAddress, $params );
        }
        $optionsWallet = $this->safe_string($this->options, 'deriveWalletAddress');
        if ($optionsWallet !== null) {
            return array( $optionsWallet, $params );
        }
        throw new ArgumentsRequired($this->id . ' ' . $methodName . '() requires a $deriveWalletAddress parameter inside \'params\' or exchange.options[\'deriveWalletAddress\'] = ADDRESS, the address can find in HOME => Developers tab.');
    }

    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
        }
        $error = $this->safe_dict($response, 'error');
        if ($error !== null) {
            $errorCode = $this->safe_string($error, 'code');
            $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);
            throw new ExchangeError($feedback);
        }
        return null;
    }

    public function sign($path, $api = 'public', $method = 'GET', $params = array (), $headers = null, $body = null) {
        $url = $this->urls['api'][$api] . '/' . $path;
        if ($method === 'POST') {
            $headers = array(
                'Content-Type' => 'application/json',
            );
            if ($api === 'private') {
                $now = (string) $this->milliseconds();
                $signature = $this->sign_message($now, $this->privateKey);
                $headers['X-LyraWallet'] = $this->safe_string($this->options, 'deriveWalletAddress');
                $headers['X-LyraTimestamp'] = $now;
                $headers['X-LyraSignature'] = $signature;
            }
            $body = $this->json($params);
        }
        return array( 'url' => $url, 'method' => $method, 'body' => $body, 'headers' => $headers );
    }
}
