# -*- coding: utf-8 -*-

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

from ccxt_zagrava.base.exchange import Exchange
from ccxt_zagrava.abstract.hyperliquid import ImplicitAPI
from ccxt_zagrava.base.types import Balances, Currencies, Currency, Int, MarginModification, Market, Num, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Str, Strings, Trade, TradingFeeInterface, Transaction, TransferEntry
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import OrderNotFound
from ccxt_zagrava.base.errors import NotSupported
from ccxt_zagrava.base.decimal_to_precision import ROUND
from ccxt_zagrava.base.decimal_to_precision import DECIMAL_PLACES
from ccxt_zagrava.base.decimal_to_precision import SIGNIFICANT_DIGITS
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class hyperliquid(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(hyperliquid, self).describe(), {
            'id': 'hyperliquid',
            'name': 'Hyperliquid',
            'countries': [],
            'version': 'v1',
            'rateLimit': 50,  # 1200 requests per minute, 20 request per second
            'certified': False,
            'pro': True,
            'dex': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': True,
                'future': True,
                'option': False,
                'addMargin': True,
                'borrowCrossMargin': False,
                'borrowIsolatedMargin': False,
                'cancelAllOrders': False,
                'cancelAllOrdersAfter': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'cancelOrdersForSymbols': True,
                'closeAllPositions': False,
                'closePosition': False,
                'createMarketBuyOrderWithCost': False,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createOrders': True,
                'createReduceOnlyOrder': True,
                'editOrder': True,
                'fetchAccounts': False,
                'fetchBalance': True,
                'fetchBorrowInterest': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchCanceledOrders': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': False,
                'fetchDepositAddresses': False,
                'fetchDeposits': False,
                'fetchDepositWithdrawFee': 'emulated',
                'fetchDepositWithdrawFees': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLedger': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchLiquidations': False,
                'fetchMarginMode': None,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyLiquidations': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': False,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': False,
                'fetchOrderTrades': False,
                'fetchPosition': True,
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': False,
                'fetchTickers': False,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': False,
                'fetchTransfer': False,
                'fetchTransfers': False,
                'fetchWithdrawal': False,
                'fetchWithdrawals': False,
                'reduceMargin': True,
                'repayCrossMargin': False,
                'repayIsolatedMargin': False,
                'sandbox': True,
                'setLeverage': True,
                'setMarginMode': True,
                'setPositionMode': False,
                'transfer': True,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1m',
                '3m': '3m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '6h': '6h',
                '12h': '12h',
                '1d': '1d',
                '3d': '3d',
                '1w': '1w',
                '1M': '1m',
            },
            'hostname': 'hyperliquid.xyz',
            'urls': {
                'logo': 'https://github.com/ccxt_zagrava/ccxt_zagrava/assets/43336371/b371bc6c-4a8c-489f-87f4-20a913dd8d4b',
                'api': {
                    'public': 'https://api.{hostname}',
                    'private': 'https://api.{hostname}',
                },
                'test': {
                    'public': 'https://api.hyperliquid-testnet.xyz',
                    'private': 'https://api.hyperliquid-testnet.xyz',
                },
                'www': 'https://hyperliquid.xyz',
                'doc': 'https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api',
                'fees': 'https://hyperliquid.gitbook.io/hyperliquid-docs/trading/fees',
                'referral': 'https://app.hyperliquid.xyz/',
            },
            'api': {
                'public': {
                    'post': {
                        'info': 1,
                    },
                },
                'private': {
                    'post': {
                        'exchange': 1,
                    },
                },
            },
            'fees': {
                'swap': {
                    'taker': self.parse_number('0.00035'),
                    'maker': self.parse_number('0.0001'),
                },
                'spot': {
                    'taker': self.parse_number('0.00035'),
                    'maker': self.parse_number('0.0001'),
                },
            },
            'requiredCredentials': {
                'apiKey': False,
                'secret': False,
                'walletAddress': True,
                'privateKey': True,
            },
            'exceptions': {
                'exact': {
                },
                'broad': {
                    'Price must be divisible by tick size.': InvalidOrder,
                    'Order must have minimum value of $10': InvalidOrder,
                    'Insufficient margin to place order.': InvalidOrder,
                    'Reduce only order would increase position.': InvalidOrder,
                    'Post only order would have immediately matched,': InvalidOrder,
                    'Order could not immediately match against any resting orders.': InvalidOrder,
                    'Invalid TP/SL price.': InvalidOrder,
                    'No liquidity available for market order.': InvalidOrder,
                    'Order was never placed, already canceled, or filled.': OrderNotFound,
                    'User or API Wallet ': InvalidOrder,
                },
            },
            'precisionMode': TICK_SIZE,
            'commonCurrencies': {
            },
            'options': {
                'defaultType': 'swap',
                'sandboxMode': False,
                'defaultSlippage': 0.05,
                'zeroAddress': '0x0000000000000000000000000000000000000000',
            },
        })

    def set_sandbox_mode(self, enabled):
        super(hyperliquid, self).set_sandbox_mode(enabled)
        self.options['sandboxMode'] = enabled

    def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-exchange-metadata
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        request: dict = {
            'type': 'meta',
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "universe": [
        #                 {
        #                     "maxLeverage": 50,
        #                     "name": "SOL",
        #                     "onlyIsolated": False,
        #                     "szDecimals": 2
        #                 }
        #             ]
        #         }
        #     ]
        #
        meta = self.safe_list(response, 'universe', [])
        result: dict = {}
        for i in range(0, len(meta)):
            data = self.safe_dict(meta, i, {})
            id = i
            name = self.safe_string(data, 'name')
            code = self.safe_currency_code(name)
            result[code] = {
                'id': id,
                'name': name,
                'code': code,
                'precision': None,
                'info': data,
                'active': None,
                'deposit': None,
                'withdraw': None,
                'networks': None,
                'fee': None,
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': None,
                        'max': None,
                    },
                },
            }
        return result

    def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for hyperliquid
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-asset-contexts-includes-mark-price-current-funding-open-interest-etc
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        rawPromises = [
            self.fetch_swap_markets(params),
            self.fetch_spot_markets(params),
        ]
        promises = rawPromises
        swapMarkets = promises[0]
        spotMarkets = promises[1]
        return self.array_concat(swapMarkets, spotMarkets)

    def fetch_swap_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all swap markets for hyperliquid
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-asset-contexts-includes-mark-price-current-funding-open-interest-etc
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        request: dict = {
            'type': 'metaAndAssetCtxs',
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "universe": [
        #                 {
        #                     "maxLeverage": 50,
        #                     "name": "SOL",
        #                     "onlyIsolated": False,
        #                     "szDecimals": 2
        #                 }
        #             ]
        #         },
        #         [
        #             {
        #                 "dayNtlVlm": "9450588.2273",
        #                 "funding": "0.0000198",
        #                 "impactPxs": [
        #                     "108.04",
        #                     "108.06"
        #                 ],
        #                 "markPx": "108.04",
        #                 "midPx": "108.05",
        #                 "openInterest": "10764.48",
        #                 "oraclePx": "107.99",
        #                 "premium": "0.00055561",
        #                 "prevDayPx": "111.81"
        #             }
        #         ]
        #     ]
        #
        #
        meta = self.safe_dict(response, 0, {})
        meta = self.safe_list(meta, 'universe', [])
        assetCtxs = self.safe_dict(response, 1, {})
        result = []
        for i in range(0, len(meta)):
            data = self.extend(
                self.safe_dict(meta, i, {}),
                self.safe_dict(assetCtxs, i, {})
            )
            data['baseId'] = i
            result.append(data)
        return self.parse_markets(result)

    def fetch_spot_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all spot markets for hyperliquid
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-asset-contexts-includes-mark-price-current-funding-open-interest-etc
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        request: dict = {
            'type': 'spotMetaAndAssetCtxs',
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        # [
        #     {
        #         'tokens': [
        #             {
        #                 'name': 'USDC',
        #                 'szDecimals': '8',
        #                 'weiDecimals': '8',
        #             },
        #             {
        #                 'name': 'PURR',
        #                 'szDecimals': '0',
        #                 'weiDecimals': '5',
        #             },
        #         ],
        #         'universe': [
        #             {
        #                 'name': 'PURR/USDC',
        #                 'tokens': [
        #                     1,
        #                     0,
        #                 ],
        #             },
        #         ],
        #     },
        #     [
        #         {
        #             'dayNtlVlm': '264250385.14640012',
        #             'markPx': '0.018314',
        #             'midPx': '0.0182235',
        #             'prevDayPx': '0.017427',
        #         },
        #     ],
        # ]
        # mainnet
        # [
        #     {
        #        "canonical_tokens2":[
        #           0,
        #           1
        #        ],
        #        "spot_infos":[
        #           {
        #              "name":"PURR/USDC",
        #              "tokens":[
        #                 1,
        #                 0
        #              ]
        #           }
        #        ],
        #        "token_id_to_token":[
        #           [
        #              "0x6d1e7cde53ba9467b783cb7c530ce054",
        #              0
        #           ],
        #           [
        #              "0xc1fb593aeffbeb02f85e0308e9956a90",
        #              1
        #           ]
        #        ],
        #        "token_infos":[
        #           {
        #              "deployer":null,
        #              "spec":{
        #                 "name":"USDC",
        #                 "szDecimals":"8",
        #                 "weiDecimals":"8"
        #              },
        #              "spots":[
        #              ]
        #           },
        #           {
        #              "deployer":null,
        #              "spec":{
        #                 "name":"PURR",
        #                 "szDecimals":"0",
        #                 "weiDecimals":"5"
        #              },
        #              "spots":[
        #                 0
        #              ]
        #           }
        #        ]
        #     },
        #     [
        #        {
        #           "dayNtlVlm":"35001170.16631",
        #           "markPx":"0.15743",
        #           "midPx":"0.157555",
        #           "prevDayPx":"0.158"
        #        }
        #     ]
        # ]
        #
        # response differs depending on the environment(mainnet vs sandbox)
        first = self.safe_dict(response, 0, {})
        meta = self.safe_list_2(first, 'universe', 'spot_infos', [])
        tokens = self.safe_list_2(first, 'tokens', 'token_infos', [])
        markets = []
        for i in range(0, len(meta)):
            market = self.safe_dict(meta, i, {})
            marketName = self.safe_string(market, 'name')
            # if marketName.find('/') < 0:
            #     # there are some weird spot markets in testnet, eg @2
            #     continue
            # }
            # marketParts = marketName.split('/')
            # baseName = self.safe_string(marketParts, 0)
            # quoteId = self.safe_string(marketParts, 1)
            fees = self.safe_dict(self.fees, 'spot', {})
            taker = self.safe_number(fees, 'taker')
            maker = self.safe_number(fees, 'maker')
            tokensPos = self.safe_list(market, 'tokens', [])
            baseTokenPos = self.safe_integer(tokensPos, 0)
            quoteTokenPos = self.safe_integer(tokensPos, 1)
            baseTokenInfo = self.safe_dict(tokens, baseTokenPos, {})
            quoteTokenInfo = self.safe_dict(tokens, quoteTokenPos, {})
            baseName = self.safe_string(baseTokenInfo, 'name')
            quoteId = self.safe_string(quoteTokenInfo, 'name')
            base = self.safe_currency_code(baseName)
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            innerBaseTokenInfo = self.safe_dict(baseTokenInfo, 'spec', baseTokenInfo)
            # innerQuoteTokenInfo = self.safe_dict(quoteTokenInfo, 'spec', quoteTokenInfo)
            amountPrecision = self.parse_number(self.parse_precision(self.safe_string(innerBaseTokenInfo, 'szDecimals')))
            # quotePrecision = self.parse_number(self.parse_precision(self.safe_string(innerQuoteTokenInfo, 'szDecimals')))
            baseId = self.number_to_string(i + 10000)
            markets.append(self.safe_market_structure({
                'id': marketName,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'type': 'spot',
                'spot': True,
                'subType': None,
                'margin': None,
                'swap': False,
                'future': False,
                'option': False,
                'active': True,
                'contract': False,
                'linear': None,
                'inverse': None,
                'taker': taker,
                'maker': maker,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': amountPrecision,  # decimal places
                    'price': 5,  # significant digits
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'created': None,
                'info': market,
            }))
        return markets

    def parse_market(self, market: dict) -> Market:
        #
        #     {
        #         "maxLeverage": "50",
        #         "name": "ETH",
        #         "onlyIsolated": False,
        #         "szDecimals": "4",
        #         "dayNtlVlm": "1709813.11535",
        #         "funding": "0.00004807",
        #         "impactPxs": [
        #             "2369.3",
        #             "2369.6"
        #         ],
        #         "markPx": "2369.6",
        #         "midPx": "2369.45",
        #         "openInterest": "1815.4712",
        #         "oraclePx": "2367.3",
        #         "premium": "0.00090821",
        #         "prevDayPx": "2381.5"
        #     }
        #
        quoteId = 'USDC'
        base = self.safe_string(market, 'name')
        quote = self.safe_currency_code(quoteId)
        baseId = self.safe_string(market, 'baseId')
        settleId = 'USDC'
        settle = self.safe_currency_code(settleId)
        symbol = base + '/' + quote
        contract = True
        swap = True
        if contract:
            if swap:
                symbol = symbol + ':' + settle
        fees = self.safe_dict(self.fees, 'swap', {})
        taker = self.safe_number(fees, 'taker')
        maker = self.safe_number(fees, 'maker')
        return {
            'id': baseId,
            'symbol': symbol,
            'base': base,
            'quote': quote,
            'settle': settle,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': settleId,
            'type': 'swap',
            'spot': False,
            'margin': None,
            'swap': swap,
            'future': False,
            'option': False,
            'active': True,
            'contract': contract,
            'linear': True,
            'inverse': False,
            'taker': taker,
            'maker': maker,
            'contractSize': self.parse_number('1'),
            'expiry': None,
            'expiryDatetime': None,
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'szDecimals'))),  # decimal places
                'price': 5,  # significant digits
            },
            'limits': {
                'leverage': {
                    'min': None,
                    'max': self.safe_integer(market, 'maxLeverage'),
                },
                'amount': {
                    'min': None,
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': None,
            'info': market,
        }

    def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.user]: user address, will default to self.walletAddress if not provided
        :param str [params.type]: wallet type, ['spot', 'swap'], defaults to swap
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchBalance', params)
        type = None
        type, params = self.handle_market_type_and_params('fetchBalance', None, params)
        isSpot = (type == 'spot')
        reqType = 'spotClearinghouseState' if (isSpot) else 'clearinghouseState'
        request: dict = {
            'type': reqType,
            'user': userAddress,
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     {
        #         "assetPositions": [],
        #         "crossMaintenanceMarginUsed": "0.0",
        #         "crossMarginSummary": {
        #             "accountValue": "100.0",
        #             "totalMarginUsed": "0.0",
        #             "totalNtlPos": "0.0",
        #             "totalRawUsd": "100.0"
        #         },
        #         "marginSummary": {
        #             "accountValue": "100.0",
        #             "totalMarginUsed": "0.0",
        #             "totalNtlPos": "0.0",
        #             "totalRawUsd": "100.0"
        #         },
        #         "time": "1704261007014",
        #         "withdrawable": "100.0"
        #     }
        # spot
        #
        #     {
        #         "balances":[
        #            {
        #               "coin":"USDC",
        #               "hold":"0.0",
        #               "total":"1481.844"
        #            },
        #            {
        #               "coin":"PURR",
        #               "hold":"0.0",
        #               "total":"999.65004"
        #            }
        #     }
        #
        balances = self.safe_list(response, 'balances')
        if balances is not None:
            spotBalances: dict = {'info': response}
            for i in range(0, len(balances)):
                balance = balances[i]
                code = self.safe_currency_code(self.safe_string(balance, 'coin'))
                account = self.account()
                total = self.safe_string(balance, 'total')
                free = self.safe_string(balance, 'hold')
                account['total'] = total
                account['free'] = free
                spotBalances[code] = account
            return self.safe_balance(spotBalances)
        data = self.safe_dict(response, 'marginSummary', {})
        result: dict = {
            'info': response,
            'USDC': {
                'total': self.safe_float(data, 'accountValue'),
                'used': self.safe_float(data, 'totalMarginUsed'),
            },
        }
        timestamp = self.safe_integer(response, 'time')
        result['timestamp'] = timestamp
        result['datetime'] = self.iso8601(timestamp)
        return self.safe_balance(result)

    def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#info
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'type': 'l2Book',
            'coin': market['base'] if market['swap'] else market['id'],
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     {
        #         "coin": "ETH",
        #         "levels": [
        #             [
        #                 {
        #                     "n": "2",
        #                     "px": "2216.2",
        #                     "sz": "74.0637"
        #                 }
        #             ],
        #             [
        #                 {
        #                     "n": "2",
        #                     "px": "2216.5",
        #                     "sz": "70.5893"
        #                 }
        #             ]
        #         ],
        #         "time": "1704290104840"
        #     }
        #
        data = self.safe_list(response, 'levels', [])
        result: dict = {
            'bids': self.safe_list(data, 0, []),
            'asks': self.safe_list(data, 1, []),
        }
        timestamp = self.safe_integer(response, 'time')
        return self.parse_order_book(result, market['symbol'], timestamp, 'bids', 'asks', 'px', 'sz')

    def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#info-1
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents, support '1m', '15m', '1h', '1d'
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest candle to fetch
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        market = self.market(symbol)
        until = self.safe_integer(params, 'until', self.milliseconds())
        if since is None:
            since = 0
        if limit is None:
            limit = 500
        params = self.omit(params, ['until'])
        request: dict = {
            'type': 'candleSnapshot',
            'req': {
                'coin': market['base'] if market['swap'] else market['id'],
                'interval': timeframe,
                'startTime': since,
                'endTime': until,
            },
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "T": 1704287699999,
        #             "c": "2226.4",
        #             "h": "2247.9",
        #             "i": "15m",
        #             "l": "2224.6",
        #             "n": 46,
        #             "o": "2247.9",
        #             "s": "ETH",
        #             "t": 1704286800000,
        #             "v": "591.6427"
        #         }
        #     ]
        #
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     {
        #         "T": 1704287699999,
        #         "c": "2226.4",
        #         "h": "2247.9",
        #         "i": "15m",
        #         "l": "2224.6",
        #         "n": 46,
        #         "o": "2247.9",
        #         "s": "ETH",
        #         "t": 1704286800000,
        #         "v": "591.6427"
        #     }
        #
        return [
            self.safe_integer(ohlcv, 't'),
            self.safe_number(ohlcv, 'o'),
            self.safe_number(ohlcv, 'h'),
            self.safe_number(ohlcv, 'l'),
            self.safe_number(ohlcv, 'c'),
            self.safe_number(ohlcv, 'v'),
        ]

    def fetch_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        get the list of most recent trades for a particular symbol
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest trade
        :param str [params.address]: wallet address that made trades
        :param str [params.user]: wallet address that made trades
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchTrades', params)
        self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'user': userAddress,
        }
        if since is not None:
            request['type'] = 'userFillsByTime'
            request['startTime'] = since
        else:
            request['type'] = 'userFills'
        until = self.safe_integer(params, 'until')
        params = self.omit(params, 'until')
        if until is not None:
            request['endTime'] = until
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "closedPnl": "0.19343",
        #             "coin": "ETH",
        #             "crossed": True,
        #             "dir": "Close Long",
        #             "fee": "0.050062",
        #             "hash": "0x09d77c96791e98b5775a04092584ab010d009445119c71e4005c0d634ea322bc",
        #             "liquidationMarkPx": null,
        #             "oid": 3929354691,
        #             "px": "2381.1",
        #             "side": "A",
        #             "startPosition": "0.0841",
        #             "sz": "0.0841",
        #             "tid": 128423918764978,
        #             "time": 1704262888911
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    def amount_to_precision(self, symbol, amount):
        market = self.market(symbol)
        if market['spot']:
            return super(hyperliquid, self).amount_to_precision(symbol, amount)
        return self.decimal_to_precision(amount, ROUND, self.markets[symbol]['precision']['amount'], self.precisionMode)

    def price_to_precision(self, symbol: str, price) -> str:
        market = self.market(symbol)
        result = self.decimal_to_precision(price, ROUND, market['precision']['price'], SIGNIFICANT_DIGITS, self.paddingMode)
        decimalParsedResult = self.decimal_to_precision(result, ROUND, 6, DECIMAL_PLACES, self.paddingMode)
        return decimalParsedResult

    def hash_message(self, message):
        return '0x' + self.hash(message, 'keccak', 'hex')

    def sign_hash(self, hash, privateKey):
        signature = self.ecdsa(hash[-64:], privateKey[-64:], 'secp256k1', None)
        return {
            'r': '0x' + signature['r'],
            's': '0x' + signature['s'],
            'v': self.sum(27, signature['v']),
        }

    def sign_message(self, message, privateKey):
        return self.sign_hash(self.hash_message(message), privateKey[-64:])

    def construct_phantom_agent(self, hash, isTestnet=True):
        source = 'b' if (isTestnet) else 'a'
        return {
            'source': source,
            'connectionId': hash,
        }

    def action_hash(self, action, vaultAddress, nonce):
        dataBinary = self.packb(action)
        dataHex = self.binary_to_base16(dataBinary)
        data = dataHex
        data += '00000' + self.int_to_base16(nonce)
        if vaultAddress is None:
            data += '00'
        else:
            data += '01'
            data += vaultAddress
        return self.hash(self.base16_to_binary(data), 'keccak', 'binary')

    def sign_l1_action(self, action, nonce, vaultAdress=None) -> object:
        hash = self.action_hash(action, vaultAdress, nonce)
        isTestnet = self.safe_bool(self.options, 'sandboxMode', False)
        phantomAgent = self.construct_phantom_agent(hash, isTestnet)
        # data: Dict = {
        #     'domain': {
        #         'chainId': 1337,
        #         'name': 'Exchange',
        #         'verifyingContract': '0x0000000000000000000000000000000000000000',
        #         'version': '1',
        #     },
        #     'types': {
        #         'Agent': [
        #             {'name': 'source', 'type': 'string'},
        #             {'name': 'connectionId', 'type': 'bytes32'},
        #         ],
        #         'EIP712Domain': [
        #             {'name': 'name', 'type': 'string'},
        #             {'name': 'version', 'type': 'string'},
        #             {'name': 'chainId', 'type': 'uint256'},
        #             {'name': 'verifyingContract', 'type': 'address'},
        #         ],
        #     },
        #     'primaryType': 'Agent',
        #     'message': phantomAgent,
        # }
        zeroAddress = self.safe_string(self.options, 'zeroAddress')
        chainId = 1337  # check self out
        domain: dict = {
            'chainId': chainId,
            'name': 'Exchange',
            'verifyingContract': zeroAddress,
            'version': '1',
        }
        messageTypes: dict = {
            'Agent': [
                {'name': 'source', 'type': 'string'},
                {'name': 'connectionId', 'type': 'bytes32'},
            ],
        }
        msg = self.eth_encode_structured_data(domain, messageTypes, phantomAgent)
        signature = self.sign_message(msg, self.privateKey)
        return signature

    def sign_user_signed_action(self, messageTypes, message):
        zeroAddress = self.safe_string(self.options, 'zeroAddress')
        chainId = 421614  # check self out
        domain: dict = {
            'chainId': chainId,
            'name': 'HyperliquidSignTransaction',
            'verifyingContract': zeroAddress,
            'version': '1',
        }
        msg = self.eth_encode_structured_data(domain, messageTypes, message)
        signature = self.sign_message(msg, self.privateKey)
        return signature

    def build_transfer_sig(self, message):
        messageTypes: dict = {
            'HyperliquidTransaction:UsdSend': [
                {'name': 'hyperliquidChain', 'type': 'string'},
                {'name': 'destination', 'type': 'string'},
                {'name': 'amount', 'type': 'string'},
                {'name': 'time', 'type': 'uint64'},
            ],
        }
        return self.sign_user_signed_action(messageTypes, message)

    def build_withdraw_sig(self, message):
        messageTypes: dict = {
            'HyperliquidTransaction:Withdraw': [
                {'name': 'hyperliquidChain', 'type': 'string'},
                {'name': 'destination', 'type': 'string'},
                {'name': 'amount', 'type': 'string'},
                {'name': 'time', 'type': 'uint64'},
            ],
        }
        return self.sign_user_signed_action(messageTypes, message)

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo'
        :param bool [params.postOnly]: True or False whether the order is post-only
        :param bool [params.reduceOnly]: True or False whether the order is reduce-only
        :param float [params.triggerPrice]: The price at which a trigger order is triggered at
        :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
        :param str [params.slippage]: the slippage for market order
        :param str [params.vaultAddress]: the vault address for order
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        vaultAddress = self.safe_string(params, 'vaultAddress')
        params = self.omit(params, 'vaultAddress')
        symbol = market['symbol']
        order = {
            'symbol': symbol,
            'type': type,
            'side': side,
            'amount': amount,
            'price': price,
            'params': params,
        }
        globalParams: dict = {}
        if vaultAddress is not None:
            globalParams['vaultAddress'] = vaultAddress
        response = self.create_orders([order], globalParams)
        first = self.safe_dict(response, 0)
        return first

    def create_orders(self, orders: List[OrderRequest], params={}):
        """
        create a list of trade orders
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
        :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        self.load_markets()
        defaultSlippage = self.safe_string(self.options, 'defaultSlippage')
        defaultSlippage = self.safe_string(params, 'slippage', defaultSlippage)
        hasClientOrderId = False
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            orderParams = self.safe_dict(rawOrder, 'params', {})
            clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
            if clientOrderId is not None:
                hasClientOrderId = True
        if hasClientOrderId:
            for i in range(0, len(orders)):
                rawOrder = orders[i]
                orderParams = self.safe_dict(rawOrder, 'params', {})
                clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
                if clientOrderId is None:
                    raise ArgumentsRequired(self.id + ' createOrders() all orders must have clientOrderId if at least one has a clientOrderId')
        params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce'])
        nonce = self.milliseconds()
        orderReq = []
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            marketId = self.safe_string(rawOrder, 'symbol')
            market = self.market(marketId)
            symbol = market['symbol']
            type = self.safe_string_upper(rawOrder, 'type')
            isMarket = (type == 'MARKET')
            side = self.safe_string_upper(rawOrder, 'side')
            isBuy = (side == 'BUY')
            amount = self.safe_string(rawOrder, 'amount')
            price = self.safe_string(rawOrder, 'price')
            orderParams = self.safe_dict(rawOrder, 'params', {})
            clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
            slippage = self.safe_string(orderParams, 'slippage', defaultSlippage)
            defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
            postOnly = self.safe_bool(orderParams, 'postOnly', False)
            if postOnly:
                defaultTimeInForce = 'alo'
            timeInForce = self.safe_string_lower(orderParams, 'timeInForce', defaultTimeInForce)
            timeInForce = self.capitalize(timeInForce)
            triggerPrice = self.safe_string_2(orderParams, 'triggerPrice', 'stopPrice')
            stopLossPrice = self.safe_string(orderParams, 'stopLossPrice', triggerPrice)
            takeProfitPrice = self.safe_string(orderParams, 'takeProfitPrice')
            isTrigger = (stopLossPrice or takeProfitPrice)
            px = None
            if isMarket:
                if price is None:
                    raise ArgumentsRequired(self.id + '  market orders require price to calculate the max slippage price. Default slippage can be set in options(default is 5%).')
                px = Precise.string_mul(price, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(price, Precise.string_sub('1', slippage))
                px = self.price_to_precision(symbol, px)  # round after adding slippage
            else:
                px = self.price_to_precision(symbol, price)
            sz = self.amount_to_precision(symbol, amount)
            reduceOnly = self.safe_bool(orderParams, 'reduceOnly', False)
            orderType: dict = {}
            if isTrigger:
                isTp = False
                if takeProfitPrice is not None:
                    triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
                    isTp = True
                else:
                    triggerPrice = self.price_to_precision(symbol, stopLossPrice)
                orderType['trigger'] = {
                    'isMarket': isMarket,
                    'triggerPx': triggerPrice,
                    'tpsl': 'tp' if (isTp) else 'sl',
                }
            else:
                orderType['limit'] = {
                    'tif': timeInForce,
                }
            orderParams = self.omit(orderParams, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly'])
            orderObj: dict = {
                'a': self.parse_to_int(market['baseId']),
                'b': isBuy,
                'p': px,
                's': sz,
                'r': reduceOnly,
                't': orderType,
                # 'c': clientOrderId,
            }
            if clientOrderId is not None:
                orderObj['c'] = clientOrderId
            orderReq.append(orderObj)
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        orderAction: dict = {
            'type': 'order',
            'orders': orderReq,
            'grouping': 'na',
            # 'brokerCode': 1,  # cant
        }
        if vaultAddress is None:
            orderAction['brokerCode'] = 1
        signature = self.sign_l1_action(orderAction, nonce, vaultAddress)
        request: dict = {
            'action': orderAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(request)
        #
        #     {
        #         "status": "ok",
        #         "response": {
        #             "type": "order",
        #             "data": {
        #                 "statuses": [
        #                     {
        #                         "resting": {
        #                             "oid": 5063830287
        #                         }
        #                     }
        #                 ]
        #             }
        #         }
        #     }
        #
        responseObj = self.safe_dict(response, 'response', {})
        data = self.safe_dict(responseObj, 'data', {})
        statuses = self.safe_list(data, 'statuses', [])
        return self.parse_orders(statuses, None)

    def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
        :param str [params.vaultAddress]: the vault address for order
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        orders = self.cancel_orders([id], symbol, params)
        return self.safe_dict(orders, 0)

    def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
        """
        cancel multiple orders
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
        :param str[] ids: order ids
        :param str [symbol]: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param string|str[] [params.clientOrderId]: client order ids,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
        :param str [params.vaultAddress]: the vault address
        :returns dict: an list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        clientOrderId = self.safe_value_2(params, 'clientOrderId', 'client_id')
        params = self.omit(params, ['clientOrderId', 'client_id'])
        nonce = self.milliseconds()
        request: dict = {
            'nonce': nonce,
            # 'vaultAddress': vaultAddress,
        }
        cancelReq = []
        cancelAction: dict = {
            'type': '',
            'cancels': [],
        }
        baseId = self.parse_to_numeric(market['baseId'])
        if clientOrderId is not None:
            if not isinstance(clientOrderId, list):
                clientOrderId = [clientOrderId]
            cancelAction['type'] = 'cancelByCloid'
            for i in range(0, len(clientOrderId)):
                cancelReq.append({
                    'asset': baseId,
                    'cloid': clientOrderId[i],
                })
        else:
            cancelAction['type'] = 'cancel'
            for i in range(0, len(ids)):
                cancelReq.append({
                    'a': baseId,
                    'o': self.parse_to_numeric(ids[i]),
                })
        cancelAction['cancels'] = cancelReq
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
        request['action'] = cancelAction
        request['signature'] = signature
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(request)
        #
        #     {
        #         "status":"ok",
        #         "response":{
        #             "type":"cancel",
        #             "data":{
        #                 "statuses":[
        #                     "success"
        #                 ]
        #             }
        #         }
        #     }
        #
        innerResponse = self.safe_dict(response, 'response')
        data = self.safe_dict(innerResponse, 'data')
        statuses = self.safe_list(data, 'statuses')
        orders = []
        for i in range(0, len(statuses)):
            status = statuses[i]
            orders.append(self.safe_order({
                'info': status,
                'status': status,
            }))
        return orders

    def cancel_orders_for_symbols(self, orders: List[CancellationRequest], params={}):
        """
        cancel multiple orders for multiple symbols
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
        :param CancellationRequest[] orders: each order should contain the parameters required by cancelOrder namely id and symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.vaultAddress]: the vault address
        :returns dict: an list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        self.load_markets()
        nonce = self.milliseconds()
        request: dict = {
            'nonce': nonce,
            # 'vaultAddress': vaultAddress,
        }
        cancelReq = []
        cancelAction: dict = {
            'type': '',
            'cancels': [],
        }
        cancelByCloid = False
        for i in range(0, len(orders)):
            order = orders[i]
            clientOrderId = self.safe_string(order, 'clientOrderId')
            if clientOrderId is not None:
                cancelByCloid = True
            id = self.safe_string(order, 'id')
            symbol = self.safe_string(order, 'symbol')
            if symbol is None:
                raise ArgumentsRequired(self.id + ' cancelOrdersForSymbols() requires a symbol argument in each order')
            if id is not None and cancelByCloid:
                raise BadRequest(self.id + ' cancelOrdersForSymbols() all orders must have either id or clientOrderId')
            assetKey = 'asset' if cancelByCloid else 'a'
            idKey = 'cloid' if cancelByCloid else 'o'
            market = self.market(symbol)
            cancelObj: dict = {}
            cancelObj[assetKey] = self.parse_to_numeric(market['baseId'])
            cancelObj[idKey] = clientOrderId if cancelByCloid else self.parse_to_numeric(id)
            cancelReq.append(cancelObj)
        cancelAction['type'] = 'cancelByCloid' if cancelByCloid else 'cancel'
        cancelAction['cancels'] = cancelReq
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
        request['action'] = cancelAction
        request['signature'] = signature
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(request)
        #
        #     {
        #         "status":"ok",
        #         "response":{
        #             "type":"cancel",
        #             "data":{
        #                 "statuses":[
        #                     "success"
        #                 ]
        #             }
        #         }
        #     }
        #
        return response

    def cancel_all_orders_after(self, timeout: Int, params={}):
        """
        dead man's switch, cancel all orders after the given timeout
        :param number timeout: time in milliseconds, 0 represents cancel the timer
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.vaultAddress]: the vault address
        :returns dict: the api result
        """
        self.check_required_credentials()
        self.load_markets()
        params = self.omit(params, ['clientOrderId', 'client_id'])
        nonce = self.milliseconds()
        request: dict = {
            'nonce': nonce,
            # 'vaultAddress': vaultAddress,
        }
        cancelAction: dict = {
            'type': 'scheduleCancel',
            'time': nonce + timeout,
        }
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
        request['action'] = cancelAction
        request['signature'] = signature
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(request)
        #
        #     {
        #         "status":"err",
        #         "response":"Cannot set scheduled cancel time until enough volume traded. Required: $1000000. Traded: $373.47205."
        #     }
        #
        return response

    def edit_order(self, id: str, symbol: str, type: str, side: str, amount: Num = None, price: Num = None, params={}):
        """
        edit a trade order
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-an-order
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
        :param str id: cancel order id
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo'
        :param bool [params.postOnly]: True or False whether the order is post-only
        :param bool [params.reduceOnly]: True or False whether the order is reduce-only
        :param float [params.triggerPrice]: The price at which a trigger order is triggered at
        :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
        :param str [params.vaultAddress]: the vault address for order
        :returns dict: an `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        if id is None:
            raise ArgumentsRequired(self.id + ' editOrder() requires an id argument')
        self.load_markets()
        market = self.market(symbol)
        type = type.upper()
        isMarket = (type == 'MARKET')
        side = side.upper()
        isBuy = (side == 'BUY')
        defaultSlippage = self.safe_string(self.options, 'defaultSlippage')
        slippage = self.safe_string(params, 'slippage', defaultSlippage)
        defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
        postOnly = self.safe_bool(params, 'postOnly', False)
        if postOnly:
            defaultTimeInForce = 'alo'
        timeInForce = self.safe_string_lower(params, 'timeInForce', defaultTimeInForce)
        timeInForce = self.capitalize(timeInForce)
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_id')
        triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
        stopLossPrice = self.safe_string(params, 'stopLossPrice', triggerPrice)
        takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
        isTrigger = (stopLossPrice or takeProfitPrice)
        params = self.omit(params, ['slippage', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'clientOrderId', 'client_id'])
        px = str(price)
        if isMarket:
            px = str(Precise.string_mul(price), Precise.string_add('1', slippage)) if (isBuy) else str(Precise.string_mul(price), Precise.string_sub('1', slippage))
        else:
            px = self.price_to_precision(symbol, str(price))
        sz = self.amount_to_precision(symbol, amount)
        reduceOnly = self.safe_bool(params, 'reduceOnly', False)
        orderType: dict = {}
        if isTrigger:
            isTp = False
            if takeProfitPrice is not None:
                triggerPrice = self.price_to_precision(symbol, takeProfitPrice)
                isTp = True
            else:
                triggerPrice = self.price_to_precision(symbol, stopLossPrice)
            orderType['trigger'] = {
                'isMarket': isMarket,
                'triggerPx': triggerPrice,
                'tpsl': 'tp' if (isTp) else 'sl',
            }
        else:
            orderType['limit'] = {
                'tif': timeInForce,
            }
        if triggerPrice is None:
            triggerPrice = '0'
        nonce = self.milliseconds()
        orderReq: dict = {
            'a': self.parse_to_int(market['baseId']),
            'b': isBuy,
            'p': px,
            's': sz,
            'r': reduceOnly,
            't': orderType,
            # 'c': clientOrderId,
        }
        if clientOrderId is not None:
            orderReq['c'] = clientOrderId
        modifyReq: dict = {
            'oid': self.parse_to_int(id),
            'order': orderReq,
        }
        modifyAction: dict = {
            'type': 'batchModify',
            'modifies': [modifyReq],
        }
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        signature = self.sign_l1_action(modifyAction, nonce, vaultAddress)
        request: dict = {
            'action': modifyAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(request)
        #
        #     {
        #         "status": "ok",
        #         "response": {
        #             "type": "order",
        #             "data": {
        #                 "statuses": [
        #                     {
        #                         "resting": {
        #                             "oid": 5063830287
        #                         }
        #                     }
        #                 ]
        #             }
        #         }
        #     }
        # when the order is filled immediately
        #     {
        #         "status":"ok",
        #         "response":{
        #            "type":"order",
        #            "data":{
        #               "statuses":[
        #                  {
        #                     "filled":{
        #                        "totalSz":"0.1",
        #                        "avgPx":"100.84",
        #                        "oid":6195281425
        #                     }
        #                  }
        #               ]
        #            }
        #         }
        #     }
        #
        responseObject = self.safe_dict(response, 'response', {})
        dataObject = self.safe_dict(responseObject, 'data', {})
        statuses = self.safe_list(dataObject, 'statuses', [])
        first = self.safe_dict(statuses, 0, {})
        return self.parse_order(first, market)

    def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches historical funding rate prices
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-historical-funding-rates
        :param str 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 <https://docs.ccxt_zagrava.com/#/?id=funding-rate-history-structure>` to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest funding rate
        :returns dict[]: a list of `funding rate structures <https://docs.ccxt_zagrava.com/#/?id=funding-rate-history-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'type': 'fundingHistory',
            'coin': market['base'],
        }
        if since is not None:
            request['startTime'] = since
        else:
            request['startTime'] = self.milliseconds() - 100 * 60 * 60 * 1000
        until = self.safe_integer(params, 'until')
        params = self.omit(params, 'until')
        if until is not None:
            request['endTime'] = until
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "coin": "ETH",
        #             "fundingRate": "0.0000125",
        #             "premium": "0.00057962",
        #             "time": 1704290400031
        #         }
        #     ]
        #
        result = []
        for i in range(0, len(response)):
            entry = response[i]
            timestamp = self.safe_integer(entry, 'time')
            result.append({
                'info': entry,
                'symbol': self.safe_symbol(None, market),
                'fundingRate': self.safe_number(entry, 'fundingRate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-open-orders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.user]: user address, will default to self.walletAddress if not provided
        :param str [params.method]: 'openOrders' or 'frontendOpenOrders' default is 'frontendOpenOrders'
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchOpenOrders', params)
        method = None
        method, params = self.handle_option_and_params(params, 'fetchOpenOrders', 'method', 'frontendOpenOrders')
        self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'type': method,
            'user': userAddress,
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "coin": "ETH",
        #             "limitPx": "2000.0",
        #             "oid": 3991946565,
        #             "origSz": "0.1",
        #             "side": "B",
        #             "sz": "0.1",
        #             "timestamp": 1704346468838
        #         }
        #     ]
        #
        return self.parse_orders(response, market, since, limit)

    def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently closed orders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.user]: user address, will default to self.walletAddress if not provided
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchClosedOrders', params)
        self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'type': 'historicalOrders',
            'user': userAddress,
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "coin": "ETH",
        #             "limitPx": "2000.0",
        #             "oid": 3991946565,
        #             "origSz": "0.1",
        #             "side": "B",
        #             "sz": "0.1",
        #             "timestamp": 1704346468838
        #         }
        #     ]
        #
        return self.parse_orders(response, market, since, limit)

    def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-order-status-by-oid-or-cloid
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.user]: user address, will default to self.walletAddress if not provided
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchOrder', params)
        self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'type': 'orderStatus',
            'oid': self.parse_to_numeric(id),
            'user': userAddress,
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     {
        #         "order": {
        #             "order": {
        #                 "children": [],
        #                 "cloid": null,
        #                 "coin": "ETH",
        #                 "isPositionTpsl": False,
        #                 "isTrigger": False,
        #                 "limitPx": "2000.0",
        #                 "oid": "3991946565",
        #                 "orderType": "Limit",
        #                 "origSz": "0.1",
        #                 "reduceOnly": False,
        #                 "side": "B",
        #                 "sz": "0.1",
        #                 "tif": "Gtc",
        #                 "timestamp": "1704346468838",
        #                 "triggerCondition": "N/A",
        #                 "triggerPx": "0.0"
        #             },
        #             "status": "open",
        #             "statusTimestamp": "1704346468838"
        #         },
        #         "status": "order"
        #     }
        #
        data = self.safe_dict(response, 'order')
        return self.parse_order(data, market)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        #  fetchOpenOrders
        #
        #     {
        #         "coin": "ETH",
        #         "limitPx": "2000.0",
        #         "oid": 3991946565,
        #         "origSz": "0.1",
        #         "side": "B",
        #         "sz": "0.1",
        #         "timestamp": 1704346468838
        #     }
        # fetchClosedorders
        #    {
        #        "cloid": null,
        #        "closedPnl": "0.0",
        #        "coin": "SOL",
        #        "crossed": True,
        #        "dir": "Open Long",
        #        "fee": "0.003879",
        #        "hash": "0x4a2647998682b7f07bc5040ab531e1011400f9a51bfa0346a0b41ebe510e8875",
        #        "liquidationMarkPx": null,
        #        "oid": "6463280784",
        #        "px": "110.83",
        #        "side": "B",
        #        "startPosition": "1.64",
        #        "sz": "0.1",
        #        "tid": "232174667018988",
        #        "time": "1709142268394"
        #    }
        #
        #  fetchOrder
        #
        #     {
        #         "order": {
        #             "children": [],
        #             "cloid": null,
        #             "coin": "ETH",
        #             "isPositionTpsl": False,
        #             "isTrigger": False,
        #             "limitPx": "2000.0",
        #             "oid": "3991946565",
        #             "orderType": "Limit",
        #             "origSz": "0.1",
        #             "reduceOnly": False,
        #             "side": "B",
        #             "sz": "0.1",
        #             "tif": "Gtc",
        #             "timestamp": "1704346468838",
        #             "triggerCondition": "N/A",
        #             "triggerPx": "0.0"
        #         },
        #         "status": "open",
        #         "statusTimestamp": "1704346468838"
        #     }
        #
        # createOrder
        #
        #     {
        #         "resting": {
        #             "oid": 5063830287
        #         }
        #     }
        #
        #     {
        #        "filled":{
        #           "totalSz":"0.1",
        #           "avgPx":"100.84",
        #           "oid":6195281425
        #        }
        #     }
        # frontendOrder
        # {
        #     "children": [],
        #     "cloid": null,
        #     "coin": "BLUR",
        #     "isPositionTpsl": False,
        #     "isTrigger": True,
        #     "limitPx": "0.5",
        #     "oid": 8670487141,
        #     "orderType": "Stop Limit",
        #     "origSz": "20.0",
        #     "reduceOnly": False,
        #     "side": "B",
        #     "sz": "20.0",
        #     "tif": null,
        #     "timestamp": 1715523663687,
        #     "triggerCondition": "Price above 0.6",
        #     "triggerPx": "0.6"
        # }
        #
        entry = self.safe_dict_n(order, ['order', 'resting', 'filled'])
        if entry is None:
            entry = order
        coin = self.safe_string(entry, 'coin')
        marketId = None
        if coin is not None:
            if coin.find('/') > -1:
                marketId = coin
            else:
                marketId = coin + '/USDC:USDC'
        if self.safe_string(entry, 'id') is None:
            market = self.safe_market(marketId, None)
        else:
            market = self.safe_market(marketId, market)
        symbol = market['symbol']
        timestamp = self.safe_integer_2(order, 'timestamp', 'statusTimestamp')
        status = self.safe_string(order, 'status')
        side = self.safe_string(entry, 'side')
        if side is not None:
            side = 'sell' if (side == 'A') else 'buy'
        return self.safe_order({
            'info': order,
            'id': self.safe_string(entry, 'oid'),
            'clientOrderId': self.safe_string(entry, 'cloid'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'lastUpdateTimestamp': None,
            'symbol': symbol,
            'type': self.parse_order_type(self.safe_string_lower(entry, 'orderType')),
            'timeInForce': self.safe_string_upper(entry, 'tif'),
            'postOnly': None,
            'reduceOnly': self.safe_bool(entry, 'reduceOnly'),
            'side': side,
            'price': self.safe_number(entry, 'limitPx'),
            'triggerPrice': self.safe_number(entry, 'triggerPx') if self.safe_bool(entry, 'isTrigger') else None,
            'amount': self.safe_number_2(entry, 'sz', 'totalSz'),
            'cost': None,
            'average': self.safe_number(entry, 'avgPx'),
            'filled': None,
            'remaining': None,
            'status': self.parse_order_status(status),
            'fee': None,
            'trades': None,
        }, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'triggered': 'open',
            'filled': 'closed',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_type(self, status):
        statuses: dict = {
            'stop limit': 'limit',
            'stop market': 'market',
        }
        return self.safe_string(statuses, status, status)

    def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all trades made by the user
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-fills-by-time
        :param str 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 dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest trade
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchMyTrades', params)
        self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'user': userAddress,
        }
        if since is not None:
            request['type'] = 'userFillsByTime'
            request['startTime'] = since
        else:
            request['type'] = 'userFills'
        until = self.safe_integer(params, 'until')
        params = self.omit(params, 'until')
        if until is not None:
            request['endTime'] = until
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "closedPnl": "0.19343",
        #             "coin": "ETH",
        #             "crossed": True,
        #             "dir": "Close Long",
        #             "fee": "0.050062",
        #             "hash": "0x09d77c96791e98b5775a04092584ab010d009445119c71e4005c0d634ea322bc",
        #             "liquidationMarkPx": null,
        #             "oid": 3929354691,
        #             "px": "2381.1",
        #             "side": "A",
        #             "startPosition": "0.0841",
        #             "sz": "0.0841",
        #             "tid": 128423918764978,
        #             "time": 1704262888911
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        #     {
        #         "closedPnl": "0.19343",
        #         "coin": "ETH",
        #         "crossed": True,
        #         "dir": "Close Long",
        #         "fee": "0.050062",
        #         "hash": "0x09d77c96791e98b5775a04092584ab010d009445119c71e4005c0d634ea322bc",
        #         "liquidationMarkPx": null,
        #         "oid": 3929354691,
        #         "px": "2381.1",
        #         "side": "A",
        #         "startPosition": "0.0841",
        #         "sz": "0.0841",
        #         "tid": 128423918764978,
        #         "time": 1704262888911
        #     }
        #
        timestamp = self.safe_integer(trade, 'time')
        price = self.safe_string(trade, 'px')
        amount = self.safe_string(trade, 'sz')
        coin = self.safe_string(trade, 'coin')
        marketId = coin + '/USDC:USDC'
        market = self.safe_market(marketId, None)
        symbol = market['symbol']
        id = self.safe_string(trade, 'tid')
        side = self.safe_string(trade, 'side')
        if side is not None:
            side = 'sell' if (side == 'A') else 'buy'
        fee = self.safe_string(trade, 'fee')
        return self.safe_trade({
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'id': id,
            'order': self.safe_string(trade, 'oid'),
            'type': None,
            'side': side,
            'takerOrMaker': None,
            'price': price,
            'amount': amount,
            'cost': None,
            'fee': {'cost': fee, 'currency': 'USDC'},
        }, market)

    def fetch_position(self, symbol: str, params={}):
        """
        fetch data on an open position
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
        :param str symbol: unified market symbol of the market the position is held in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.user]: user address, will default to self.walletAddress if not provided
        :returns dict: a `position structure <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        positions = self.fetch_positions([symbol], params)
        return self.safe_dict(positions, 0, {})

    def fetch_positions(self, symbols: Strings = None, params={}):
        """
        fetch all open positions
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-state
        :param str[] [symbols]: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.user]: user address, will default to self.walletAddress if not provided
        :returns dict[]: a list of `position structure <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        self.load_markets()
        userAddress = None
        userAddress, params = self.handle_public_address('fetchPositions', params)
        symbols = self.market_symbols(symbols)
        request: dict = {
            'type': 'clearinghouseState',
            'user': userAddress,
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     {
        #         "assetPositions": [
        #             {
        #                 "position": {
        #                     "coin": "ETH",
        #                     "cumFunding": {
        #                         "allTime": "0.0",
        #                         "sinceChange": "0.0",
        #                         "sinceOpen": "0.0"
        #                     },
        #                     "entryPx": "2213.9",
        #                     "leverage": {
        #                         "rawUsd": "-475.23904",
        #                         "type": "isolated",
        #                         "value": "20"
        #                     },
        #                     "liquidationPx": "2125.00856238",
        #                     "marginUsed": "24.88097",
        #                     "maxLeverage": "50",
        #                     "positionValue": "500.12001",
        #                     "returnOnEquity": "0.0",
        #                     "szi": "0.2259",
        #                     "unrealizedPnl": "0.0"
        #                 },
        #                 "type": "oneWay"
        #             }
        #         ],
        #         "crossMaintenanceMarginUsed": "0.0",
        #         "crossMarginSummary": {
        #             "accountValue": "100.0",
        #             "totalMarginUsed": "0.0",
        #             "totalNtlPos": "0.0",
        #             "totalRawUsd": "100.0"
        #         },
        #         "marginSummary": {
        #             "accountValue": "100.0",
        #             "totalMarginUsed": "0.0",
        #             "totalNtlPos": "0.0",
        #             "totalRawUsd": "100.0"
        #         },
        #         "time": "1704261007014",
        #         "withdrawable": "100.0"
        #     }
        #
        data = self.safe_list(response, 'assetPositions', [])
        result = []
        for i in range(0, len(data)):
            result.append(self.parse_position(data[i], None))
        return self.filter_by_array_positions(result, 'symbol', symbols, False)

    def parse_position(self, position: dict, market: Market = None):
        #
        #     {
        #         "position": {
        #             "coin": "ETH",
        #             "cumFunding": {
        #                 "allTime": "0.0",
        #                 "sinceChange": "0.0",
        #                 "sinceOpen": "0.0"
        #             },
        #             "entryPx": "2213.9",
        #             "leverage": {
        #                 "rawUsd": "-475.23904",
        #                 "type": "isolated",
        #                 "value": "20"
        #             },
        #             "liquidationPx": "2125.00856238",
        #             "marginUsed": "24.88097",
        #             "maxLeverage": "50",
        #             "positionValue": "500.12001",
        #             "returnOnEquity": "0.0",
        #             "szi": "0.2259",
        #             "unrealizedPnl": "0.0"
        #         },
        #         "type": "oneWay"
        #     }
        #
        entry = self.safe_dict(position, 'position', {})
        coin = self.safe_string(entry, 'coin')
        marketId = coin + '/USDC:USDC'
        market = self.safe_market(marketId, None)
        symbol = market['symbol']
        leverage = self.safe_dict(entry, 'leverage', {})
        isIsolated = (self.safe_string(leverage, 'type') == 'isolated')
        quantity = self.safe_number(leverage, 'rawUsd')
        side = None
        if quantity is not None:
            side = 'short' if (quantity > 0) else 'long'
        unrealizedPnl = self.safe_number(entry, 'unrealizedPnl')
        initialMargin = self.safe_number(entry, 'marginUsed')
        percentage = unrealizedPnl / initialMargin * 100
        return self.safe_position({
            'info': position,
            'id': None,
            'symbol': symbol,
            'timestamp': None,
            'datetime': None,
            'isolated': isIsolated,
            'hedged': None,
            'side': side,
            'contracts': self.safe_number(entry, 'szi'),
            'contractSize': None,
            'entryPrice': self.safe_number(entry, 'entryPx'),
            'markPrice': None,
            'notional': self.safe_number(entry, 'positionValue'),
            'leverage': self.safe_number(leverage, 'value'),
            'collateral': None,
            'initialMargin': initialMargin,
            'maintenanceMargin': None,
            'initialMarginPercentage': None,
            'maintenanceMarginPercentage': None,
            'unrealizedPnl': unrealizedPnl,
            'liquidationPrice': self.safe_number(entry, 'liquidationPx'),
            'marginMode': None,
            'percentage': percentage,
        })

    def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
        """
        set margin mode(symbol)
        :param str marginMode: margin mode must be either [isolated, cross]
        :param str symbol: unified market symbol of the market the position is held in, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.leverage]: the rate of leverage, is required if setting trade mode(symbol)
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        leverage = self.safe_integer(params, 'leverage')
        if leverage is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires a leverage parameter')
        asset = self.parse_to_int(market['baseId'])
        isCross = (marginMode == 'cross')
        nonce = self.milliseconds()
        params = self.omit(params, ['leverage'])
        updateAction: dict = {
            'type': 'updateLeverage',
            'asset': asset,
            'isCross': isCross,
            'leverage': leverage,
        }
        vaultAddress = self.safe_string(params, 'vaultAddress')
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            if vaultAddress.startswith('0x'):
                vaultAddress = vaultAddress.replace('0x', '')
        signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
        request: dict = {
            'action': updateAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(request)
        #
        #     {
        #         'response': {
        #             'type': 'default'
        #         },
        #         'status': 'ok'
        #     }
        #
        return response

    def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
        """
        set the level of leverage for a market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.marginMode]: margin mode must be either [isolated, cross], default is cross
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        marginMode = self.safe_string(params, 'marginMode', 'cross')
        isCross = (marginMode == 'cross')
        asset = self.parse_to_int(market['baseId'])
        nonce = self.milliseconds()
        params = self.omit(params, 'marginMode')
        updateAction: dict = {
            'type': 'updateLeverage',
            'asset': asset,
            'isCross': isCross,
            'leverage': leverage,
        }
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
        request: dict = {
            'action': updateAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(request)
        #
        #     {
        #         'response': {
        #             'type': 'default'
        #         },
        #         'status': 'ok'
        #     }
        #
        return response

    def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
        """
        add margin
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
        :param str symbol: unified market symbol
        :param float amount: amount of margin to add
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `margin structure <https://docs.ccxt_zagrava.com/#/?id=add-margin-structure>`
        """
        return self.modify_margin_helper(symbol, amount, 'add', params)

    def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
        """
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
        remove margin from a position
        :param str symbol: unified market symbol
        :param float amount: the amount of margin to remove
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `margin structure <https://docs.ccxt_zagrava.com/#/?id=reduce-margin-structure>`
        """
        return self.modify_margin_helper(symbol, amount, 'reduce', params)

    def modify_margin_helper(self, symbol: str, amount, type, params={}) -> MarginModification:
        self.load_markets()
        market = self.market(symbol)
        asset = self.parse_to_int(market['baseId'])
        sz = self.parse_to_int(Precise.string_mul(self.amount_to_precision(symbol, amount), '1000000'))
        if type == 'reduce':
            sz = -sz
        nonce = self.milliseconds()
        updateAction: dict = {
            'type': 'updateIsolatedMargin',
            'asset': asset,
            'isBuy': True,
            'ntli': sz,
        }
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        signature = self.sign_l1_action(updateAction, nonce, vaultAddress)
        request: dict = {
            'action': updateAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(request)
        #
        #     {
        #         'response': {
        #             'type': 'default'
        #         },
        #         'status': 'ok'
        #     }
        #
        return self.extend(self.parse_margin_modification(response, market), {
            'code': self.safe_string(response, 'status'),
        })

    def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
        #
        #    {
        #        'type': 'default'
        #    }
        #
        return {
            'info': data,
            'symbol': self.safe_symbol(None, market),
            'type': None,
            'marginMode': 'isolated',
            'amount': None,
            'total': None,
            'code': self.safe_string(market, 'settle'),
            'status': None,
            'timestamp': None,
            'datetime': None,
        }

    def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#l1-usdc-transfer
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from *spot, swap*
        :param str toAccount: account to transfer to *swap, spot or address*
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.vaultAddress]: the vault address for order
        :returns dict: a `transfer structure <https://docs.ccxt_zagrava.com/#/?id=transfer-structure>`
        """
        self.check_required_credentials()
        self.load_markets()
        isSandboxMode = self.safe_bool(self.options, 'sandboxMode')
        nonce = self.milliseconds()
        if self.in_array(fromAccount, ['spot', 'swap', 'perp']):
            # handle swap <> spot account transfer
            if not self.in_array(toAccount, ['spot', 'swap', 'perp']):
                raise NotSupported(self.id + 'transfer() only support spot <> swap transfer')
            vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
            params = self.omit(params, 'vaultAddress')
            toPerp = (toAccount == 'perp') or (toAccount == 'swap')
            action: dict = {
                'type': 'spotUser',
                'classTransfer': {
                    'usdc': amount,
                    'toPerp': toPerp,
                },
            }
            signature = self.sign_l1_action(action, nonce, vaultAddress)
            innerRequest: dict = {
                'action': action,
                'nonce': nonce,
                'signature': signature,
            }
            if vaultAddress is not None:
                innerRequest['vaultAddress'] = vaultAddress
            transferResponse = self.privatePostExchange(innerRequest)
            return transferResponse
        # handle sub-account/different account transfer
        self.check_address(toAccount)
        if code is not None:
            code = code.upper()
            if code != 'USDC':
                raise NotSupported(self.id + 'transfer() only support USDC')
        payload: dict = {
            'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
            'destination': toAccount,
            'amount': self.number_to_string(amount),
            'time': nonce,
        }
        sig = self.build_transfer_sig(payload)
        request: dict = {
            'action': {
                'hyperliquidChain': payload['hyperliquidChain'],
                'signatureChainId': '0x66eee',  # check self out
                'destination': toAccount,
                'amount': str(amount),
                'time': nonce,
                'type': 'usdSend',
            },
            'nonce': nonce,
            'signature': sig,
        }
        response = self.privatePostExchange(request)
        return response

    def withdraw(self, code: str, amount: float, address: str, tag=None, params={}) -> Transaction:
        """
        make a withdrawal(only support USDC)
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#initiate-a-withdrawal-request
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        self.check_required_credentials()
        self.load_markets()
        self.check_address(address)
        if code is not None:
            code = code.upper()
            if code != 'USDC':
                raise NotSupported(self.id + 'withdraw() only support USDC')
        isSandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
        nonce = self.milliseconds()
        payload: dict = {
            'hyperliquidChain': 'Testnet' if isSandboxMode else 'Mainnet',
            'destination': address,
            'amount': str(amount),
            'time': nonce,
        }
        sig = self.build_withdraw_sig(payload)
        request: dict = {
            'action': {
                'hyperliquidChain': payload['hyperliquidChain'],
                'signatureChainId': '0x66eee',  # check self out
                'destination': address,
                'amount': str(amount),
                'time': nonce,
                'type': 'withdraw3',
            },
            'nonce': nonce,
            'signature': sig,
        }
        response = self.privatePostExchange(request)
        return self.parse_transaction(response)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # {status: 'ok', response: {type: 'default'}}
        #
        return {
            'info': transaction,
            'id': None,
            'txid': None,
            'timestamp': None,
            'datetime': None,
            'network': None,
            'address': None,
            'addressTo': None,
            'addressFrom': None,
            'tag': None,
            'tagTo': None,
            'tagFrom': None,
            'type': None,
            'amount': None,
            'currency': None,
            'status': self.safe_string(transaction, 'status'),
            'updated': None,
            'comment': None,
            'internal': None,
            'fee': None,
        }

    def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
        """
        fetch the trading fees for a market
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.user]: user address, will default to self.walletAddress if not provided
        :returns dict: a `fee structure <https://docs.ccxt_zagrava.com/#/?id=fee-structure>`
        """
        self.load_markets()
        userAddress = None
        userAddress, params = self.handle_public_address('fetchTradingFee', params)
        market = self.market(symbol)
        request: dict = {
            'type': 'userFees',
            'user': userAddress,
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     {
        #         "dailyUserVlm": [
        #             {
        #                 "date": "2024-07-08",
        #                 "userCross": "0.0",
        #                 "userAdd": "0.0",
        #                 "exchange": "90597185.23639999"
        #             }
        #         ],
        #         "feeSchedule": {
        #             "cross": "0.00035",
        #             "add": "0.0001",
        #             "tiers": {
        #                 "vip": [
        #                     {
        #                         "ntlCutoff": "5000000.0",
        #                         "cross": "0.0003",
        #                         "add": "0.00005"
        #                     }
        #                 ],
        #                 "mm": [
        #                     {
        #                         "makerFractionCutoff": "0.005",
        #                         "add": "-0.00001"
        #                     }
        #                 ]
        #             },
        #             "referralDiscount": "0.04"
        #         },
        #         "userCrossRate": "0.00035",
        #         "userAddRate": "0.0001",
        #         "activeReferralDiscount": "0.0"
        #     }
        #
        data: dict = {
            'userCrossRate': self.safe_string(response, 'userCrossRate'),
            'userAddRate': self.safe_string(response, 'userAddRate'),
        }
        return self.parse_trading_fee(data, market)

    def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
        #
        #     {
        #         "dailyUserVlm": [
        #             {
        #                 "date": "2024-07-08",
        #                 "userCross": "0.0",
        #                 "userAdd": "0.0",
        #                 "exchange": "90597185.23639999"
        #             }
        #         ],
        #         "feeSchedule": {
        #             "cross": "0.00035",
        #             "add": "0.0001",
        #             "tiers": {
        #                 "vip": [
        #                     {
        #                         "ntlCutoff": "5000000.0",
        #                         "cross": "0.0003",
        #                         "add": "0.00005"
        #                     }
        #                 ],
        #                 "mm": [
        #                     {
        #                         "makerFractionCutoff": "0.005",
        #                         "add": "-0.00001"
        #                     }
        #                 ]
        #             },
        #             "referralDiscount": "0.04"
        #         },
        #         "userCrossRate": "0.00035",
        #         "userAddRate": "0.0001",
        #         "activeReferralDiscount": "0.0"
        #     }
        #
        symbol = self.safe_symbol(None, market)
        return {
            'info': fee,
            'symbol': symbol,
            'maker': self.safe_number(fee, 'userAddRate'),
            'taker': self.safe_number(fee, 'userCrossRate'),
            'percentage': None,
            'tierBased': None,
        }

    def format_vault_address(self, address: Str = None):
        if address is None:
            return None
        if address.startswith('0x'):
            return address.replace('0x', '')
        return address

    def handle_public_address(self, methodName: str, params: dict):
        userAux = None
        userAux, params = self.handle_option_and_params(params, methodName, 'user')
        user = userAux
        user, params = self.handle_option_and_params(params, methodName, 'address', userAux)
        if (user is not None) and (user != ''):
            return [user, params]
        if (self.walletAddress is not None) and (self.walletAddress != ''):
            return [self.walletAddress, params]
        raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a user parameter inside \'params\' or the wallet address set')

    def coin_to_market_id(self, coin: Str):
        if coin.find('/') > -1:
            return coin  # spot
        return coin + '/USDC:USDC'

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if not response:
            return None  # fallback to default error handler
        # {"status":"err","response":"User or API Wallet 0xb8a6f8b26223de27c31938d56e470a5b832703a5 does not exist."}
        #
        #     {
        #         status: 'ok',
        #         response: {type: 'order', data: {statuses: [{error: 'Insufficient margin to place order. asset=4'}]}}
        #     }
        #
        status = self.safe_string(response, 'status', '')
        message = None
        if status == 'err':
            message = self.safe_string(response, 'response')
        else:
            responsePayload = self.safe_dict(response, 'response', {})
            data = self.safe_dict(responsePayload, 'data', {})
            statuses = self.safe_list(data, 'statuses', [])
            firstStatus = self.safe_dict(statuses, 0)
            message = self.safe_string(firstStatus, 'error')
        feedback = self.id + ' ' + body
        nonEmptyMessage = ((message is not None) and (message != ''))
        if nonEmptyMessage:
            self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
        if nonEmptyMessage:
            raise ExchangeError(feedback)  # unknown message
        return None

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.implode_hostname(self.urls['api'][api]) + '/' + path
        if method == 'POST':
            headers = {
                'Content-Type': 'application/json',
            }
            body = self.json(params)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}
