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

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

from ccxt.async_support.base.exchange import Exchange
from ccxt.abstract.hyperliquid import ImplicitAPI
import asyncio
from ccxt.base.types import Balances, Currencies, Currency, Int, MarginModification, Market, Num, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Str, Strings, Trade, Transaction, TransferEntry
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import NotSupported
from ccxt.base.decimal_to_precision import ROUND
from ccxt.base.decimal_to_precision import DECIMAL_PLACES
from ccxt.base.decimal_to_precision import SIGNIFICANT_DIGITS
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.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': False,
                '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/ccxt/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

    async 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 = await 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,
                # 'fees': fees,
                'limits': None,
            }
        return result

    async 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 = await asyncio.gather(*rawPromises)
        swapMarkets = promises[0]
        spotMarkets = promises[1]
        return self.array_concat(swapMarkets, spotMarkets)

    async 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 = await 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)

    async 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 = await 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)
            base = self.safe_currency_code(baseName)
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            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, {})
            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': None,
                },
                'amount': {
                    'min': None,
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': None,
            'info': market,
        }

    async 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.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 = await 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)

    async 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.com/#/?id=order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'type': 'l2Book',
            'coin': market['base'] if market['swap'] else market['id'],
        }
        response = await 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')

    async 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
        """
        await 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 = await 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'),
        ]

    async 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.com/#/?id=trade-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchTrades', params)
        await 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 = await 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)

    async 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 fullfilled, 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.com/#/?id=order-structure>`
        """
        await 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 = await self.create_orders([order], globalParams)
        first = self.safe_dict(response, 0)
        return first

    async 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.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        await 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(self.extend(orderObj, orderParams))
        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 = await self.privatePostExchange(self.extend(request, params))
        #
        #     {
        #         "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)

    async 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.com/#/?id=order-structure>`
        """
        return await self.cancel_orders([id], symbol, params)

    async 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.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
        await 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 = await self.privatePostExchange(self.extend(request, params))
        #
        #     {
        #         "status":"ok",
        #         "response":{
        #             "type":"cancel",
        #             "data":{
        #                 "statuses":[
        #                     "success"
        #                 ]
        #             }
        #         }
        #     }
        #
        return response

    async 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
        :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.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        await 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 = await self.privatePostExchange(self.extend(request, params))
        #
        #     {
        #         "status":"ok",
        #         "response":{
        #             "type":"cancel",
        #             "data":{
        #                 "statuses":[
        #                     "success"
        #                 ]
        #             }
        #         }
        #     }
        #
        return response

    async 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()
        await 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 = await self.privatePostExchange(self.extend(request, params))
        #
        #     {
        #         "status":"err",
        #         "response":"Cannot set scheduled cancel time until enough volume traded. Required: $1000000. Traded: $373.47205."
        #     }
        #
        return response

    async 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 fullfilled, in units of the base 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.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        if id is None:
            raise ArgumentsRequired(self.id + ' editOrder() requires an id argument')
        await 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 = await self.privatePostExchange(self.extend(request, params))
        #
        #     {
        #         "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)

    async 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.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.com/#/?id=funding-rate-history-structure>`
        """
        await 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 = await 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)

    async 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.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')
        await self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'type': method,
            'user': userAddress,
        }
        response = await 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)

    async 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.com/#/?id=order-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchClosedOrders', params)
        await self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'type': 'historicalOrders',
            'user': userAddress,
        }
        response = await 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)

    async 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.com/#/?id=order-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchOrder', params)
        await self.load_markets()
        market = self.safe_market(symbol)
        request: dict = {
            'type': 'orderStatus',
            'oid': self.parse_to_numeric(id),
            'user': userAddress,
        }
        response = await 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)

    async 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.com/#/?id=trade-structure>`
        """
        userAddress = None
        userAddress, params = self.handle_public_address('fetchMyTrades', params)
        await 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 = await 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)

    async 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.com/#/?id=position-structure>`
        """
        positions = await self.fetch_positions([symbol], params)
        return self.safe_dict(positions, 0, {})

    async 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.com/#/?id=position-structure>`
        """
        await 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 = await 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,
        })

    async 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')
        await 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', '')
        extendedAction = self.extend(updateAction, params)
        signature = self.sign_l1_action(extendedAction, nonce, vaultAddress)
        request: dict = {
            'action': extendedAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            request['vaultAddress'] = vaultAddress
        response = await self.privatePostExchange(request)
        #
        #     {
        #         'response': {
        #             'type': 'default'
        #         },
        #         'status': 'ok'
        #     }
        #
        return response

    async 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')
        await 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 = await self.privatePostExchange(self.extend(request, params))
        #
        #     {
        #         'response': {
        #             'type': 'default'
        #         },
        #         'status': 'ok'
        #     }
        #
        return response

    async 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.com/#/?id=add-margin-structure>`
        """
        return await self.modify_margin_helper(symbol, amount, 'add', params)

    async 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.com/#/?id=reduce-margin-structure>`
        """
        return await self.modify_margin_helper(symbol, amount, 'reduce', params)

    async def modify_margin_helper(self, symbol: str, amount, type, params={}) -> MarginModification:
        await 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 = await self.privatePostExchange(self.extend(request, params))
        #
        #     {
        #         '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,
        }

    async 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.com/#/?id=transfer-structure>`
        """
        self.check_required_credentials()
        await 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': self.extend(action, params),
                'nonce': nonce,
                'signature': signature,
            }
            if vaultAddress is not None:
                innerRequest['vaultAddress'] = vaultAddress
            transferResponse = await 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 = await self.privatePostExchange(self.extend(request, params))
        return response

    async 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.com/#/?id=transaction-structure>`
        """
        self.check_required_credentials()
        await 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 = await self.privatePostExchange(self.extend(request, params))
        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 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}
