# -*- 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.base.exchange import Exchange
from ccxt.abstract.hyperliquid import ImplicitAPI
from ccxt.base.types import Balances, Currencies, Int, MarginModification, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Trade, TransferEntry
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import ArgumentsRequired
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,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': True,
                'future': True,
                'option': False,
                'addMargin': True,
                'borrowCrossMargin': False,
                'borrowIsolatedMargin': False,
                'cancelAllOrders': False,
                'cancelOrder': True,
                'cancelOrders': 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,
                '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

    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 = {
            'type': 'meta',
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "universe": [
        #                 {
        #                     "maxLeverage": 50,
        #                     "name": "SOL",
        #                     "onlyIsolated": False,
        #                     "szDecimals": 2
        #                 }
        #             ]
        #         }
        #     ]
        #
        meta = self.safe_list(response, 'universe', [])
        result = {}
        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

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

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

    def fetch_spot_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all spot markets for hyperliquid
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-asset-contexts-includes-mark-price-current-funding-open-interest-etc
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        request = {
            'type': 'spotMetaAndAssetCtxs',
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        # [
        #     {
        #         'tokens': [
        #             {
        #                 'name': 'USDC',
        #                 'szDecimals': '8',
        #                 'weiDecimals': '8',
        #             },
        #             {
        #                 'name': 'PURR',
        #                 'szDecimals': '0',
        #                 'weiDecimals': '5',
        #             },
        #         ],
        #         'universe': [
        #             {
        #                 'name': 'PURR/USDC',
        #                 'tokens': [
        #                     1,
        #                     0,
        #                 ],
        #             },
        #         ],
        #     },
        #     [
        #         {
        #             'dayNtlVlm': '264250385.14640012',
        #             'markPx': '0.018314',
        #             'midPx': '0.0182235',
        #             'prevDayPx': '0.017427',
        #         },
        #     ],
        # ]
        #
        first = self.safe_dict(response, 0, {})
        meta = self.safe_list(first, 'universe', [])
        tokens = self.safe_list(first, 'tokens', [])
        markets = []
        for i in range(0, len(meta)):
            market = self.safe_dict(meta, i, {})
            marketName = self.safe_string(market, 'name')
            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, {})
            baseDecimals = self.safe_string(baseTokenInfo, 'szDecimals')
            quoteDecimals = self.safe_integer(quoteTokenInfo, 'szDecimals')
            baseId = self.number_to_string(i + 10000)
            markets.append(self.safe_market_structure({
                'id': baseId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'type': 'spot',
                'spot': True,
                'margin': None,
                'swap': False,
                'future': False,
                'option': False,
                'active': True,
                'contract': False,
                'linear': True,
                'inverse': False,
                'taker': taker,
                'maker': maker,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.parse_number(self.parse_precision(baseDecimals)),  # decimal places
                    'price': quoteDecimals,  # 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) -> 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,
        }

    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 = {
            'type': reqType,
            'user': userAddress,
        }
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     {
        #         "assetPositions": [],
        #         "crossMaintenanceMarginUsed": "0.0",
        #         "crossMarginSummary": {
        #             "accountValue": "100.0",
        #             "totalMarginUsed": "0.0",
        #             "totalNtlPos": "0.0",
        #             "totalRawUsd": "100.0"
        #         },
        #         "marginSummary": {
        #             "accountValue": "100.0",
        #             "totalMarginUsed": "0.0",
        #             "totalNtlPos": "0.0",
        #             "totalRawUsd": "100.0"
        #         },
        #         "time": "1704261007014",
        #         "withdrawable": "100.0"
        #     }
        # spot
        #
        #     {
        #         "balances":[
        #            {
        #               "coin":"USDC",
        #               "hold":"0.0",
        #               "total":"1481.844"
        #            },
        #            {
        #               "coin":"PURR",
        #               "hold":"0.0",
        #               "total":"999.65004"
        #            }
        #     }
        #
        balances = self.safe_list(response, 'balances')
        if balances is not None:
            spotBalances = {'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 = {
            'info': response,
            'USDC': {
                'total': self.safe_float(data, 'accountValue'),
                'used': self.safe_float(data, 'totalMarginUsed'),
            },
        }
        timestamp = self.safe_integer(response, 'time')
        result['timestamp'] = timestamp
        result['datetime'] = self.iso8601(timestamp)
        return self.safe_balance(result)

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

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

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

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

    def amount_to_precision(self, symbol, amount):
        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 = {
        #     '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 = {
            'chainId': chainId,
            'name': 'Exchange',
            'verifyingContract': zeroAddress,
            'version': '1',
        }
        messageTypes = {
            '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 build_sig(self, chainId, messageTypes, message):
        zeroAddress = self.safe_string(self.options, 'zeroAddress')
        domain = {
            'chainId': chainId,
            'name': 'Exchange',
            '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):
        isSandboxMode = self.safe_bool(self.options, 'sandboxMode')
        chainId = 421614 if (isSandboxMode) else 42161
        messageTypes = {
            'UsdTransferSignPayload': [
                {'name': 'destination', 'type': 'string'},
                {'name': 'amount', 'type': 'string'},
                {'name': 'time', 'type': 'uint64'},
            ],
        }
        return self.build_sig(chainId, messageTypes, message)

    def build_withdraw_sig(self, message):
        isSandboxMode = self.safe_bool(self.options, 'sandboxMode')
        chainId = 421614 if (isSandboxMode) else 42161
        messageTypes = {
            'WithdrawFromBridge2SignPayload': [
                {'name': 'destination', 'type': 'string'},
                {'name': 'usd', 'type': 'string'},
                {'name': 'time', 'type': 'uint64'},
            ],
        }
        return self.build_sig(chainId, messageTypes, message)

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be 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>`
        """
        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 = {}
        if vaultAddress is not None:
            globalParams['vaultAddress'] = vaultAddress
        response = self.create_orders([order], globalParams)
        first = self.safe_dict(response, 0)
        return first

    def create_orders(self, orders: List[OrderRequest], params={}):
        """
        create a list of trade orders
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
        :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.check_required_credentials()
        self.load_markets()
        defaultSlippage = self.safe_string(self.options, 'defaultSlippage')
        defaultSlippage = self.safe_string(params, 'slippage', defaultSlippage)
        hasClientOrderId = False
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            orderParams = self.safe_dict(rawOrder, 'params', {})
            clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
            if clientOrderId is not None:
                hasClientOrderId = True
        if hasClientOrderId:
            for i in range(0, len(orders)):
                rawOrder = orders[i]
                orderParams = self.safe_dict(rawOrder, 'params', {})
                clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
                if clientOrderId is None:
                    raise ArgumentsRequired(self.id + ' createOrders() all orders must have clientOrderId if at least one has a clientOrderId')
        params = self.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce'])
        nonce = self.milliseconds()
        orderReq = []
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            marketId = self.safe_string(rawOrder, 'symbol')
            market = self.market(marketId)
            symbol = market['symbol']
            type = self.safe_string_upper(rawOrder, 'type')
            isMarket = (type == 'MARKET')
            side = self.safe_string_upper(rawOrder, 'side')
            isBuy = (side == 'BUY')
            amount = self.safe_string(rawOrder, 'amount')
            price = self.safe_string(rawOrder, 'price')
            orderParams = self.safe_dict(rawOrder, 'params', {})
            clientOrderId = self.safe_string_2(orderParams, 'clientOrderId', 'client_id')
            slippage = self.safe_string(orderParams, 'slippage', defaultSlippage)
            defaultTimeInForce = 'ioc' if (isMarket) else 'gtc'
            postOnly = self.safe_bool(orderParams, 'postOnly', False)
            if postOnly:
                defaultTimeInForce = 'alo'
            timeInForce = self.safe_string_lower(orderParams, 'timeInForce', defaultTimeInForce)
            timeInForce = self.capitalize(timeInForce)
            triggerPrice = self.safe_string_2(orderParams, 'triggerPrice', 'stopPrice')
            stopLossPrice = self.safe_string(orderParams, 'stopLossPrice', triggerPrice)
            takeProfitPrice = self.safe_string(orderParams, 'takeProfitPrice')
            isTrigger = (stopLossPrice or takeProfitPrice)
            px = None
            if isMarket:
                if price is None:
                    raise ArgumentsRequired(self.id + '  market orders require price to calculate the max slippage price. Default slippage can be set in options(default is 5%).')
                px = Precise.string_mul(price, Precise.string_add('1', slippage)) if (isBuy) else Precise.string_mul(price, Precise.string_sub('1', slippage))
                px = self.price_to_precision(symbol, px)  # round after adding slippage
            else:
                px = self.price_to_precision(symbol, price)
            sz = self.amount_to_precision(symbol, amount)
            reduceOnly = self.safe_bool(orderParams, 'reduceOnly', False)
            orderType = {}
            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 = {
                '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 = {
            '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 = {
            'action': orderAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = 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)

    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 self.cancel_orders([id], symbol, params)

    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')
        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 = {
            'nonce': nonce,
            # 'vaultAddress': vaultAddress,
        }
        cancelReq = []
        cancelAction = {
            'type': '',
            'cancels': [],
        }
        baseId = self.parse_to_numeric(market['baseId'])
        if clientOrderId is not None:
            if not isinstance(clientOrderId, list):
                clientOrderId = [clientOrderId]
            cancelAction['type'] = 'cancelByCloid'
            for i in range(0, len(clientOrderId)):
                cancelReq.append({
                    'asset': baseId,
                    'cloid': clientOrderId[i],
                })
        else:
            cancelAction['type'] = 'cancel'
            for i in range(0, len(ids)):
                cancelReq.append({
                    'a': baseId,
                    'o': self.parse_to_numeric(ids[i]),
                })
        cancelAction['cancels'] = cancelReq
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        signature = self.sign_l1_action(cancelAction, nonce, vaultAddress)
        request['action'] = cancelAction
        request['signature'] = signature
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = self.privatePostExchange(self.extend(request, params))
        #
        #     {
        #         "status":"ok",
        #         "response":{
        #             "type":"cancel",
        #             "data":{
        #                 "statuses":[
        #                     "success"
        #                 ]
        #             }
        #         }
        #     }
        #
        return response

    def edit_order(self, id: str, symbol: str, type: str, side: str, amount: Num = None, price: Num = None, params={}):
        """
        edit a trade order
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-an-order
        :see: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
        :param str id: cancel order id
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be 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')
        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 = {}
        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 = {
            '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 = {
            'oid': self.parse_to_int(id),
            'order': orderReq,
        }
        modifyAction = {
            'type': 'batchModify',
            'modifies': [modifyReq],
        }
        vaultAddress = self.format_vault_address(self.safe_string(params, 'vaultAddress'))
        signature = self.sign_l1_action(modifyAction, nonce, vaultAddress)
        request = {
            'action': modifyAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = 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)

    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>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'type': 'fundingHistory',
            'coin': market['base'],
        }
        if since is not None:
            request['startTime'] = since
        else:
            request['startTime'] = self.milliseconds() - 100 * 60 * 60 * 1000
        until = self.safe_integer(params, 'until')
        params = self.omit(params, 'until')
        if until is not None:
            request['endTime'] = until
        response = self.publicPostInfo(self.extend(request, params))
        #
        #     [
        #         {
        #             "coin": "ETH",
        #             "fundingRate": "0.0000125",
        #             "premium": "0.00057962",
        #             "time": 1704290400031
        #         }
        #     ]
        #
        result = []
        for i in range(0, len(response)):
            entry = response[i]
            timestamp = self.safe_integer(entry, 'time')
            result.append({
                'info': entry,
                'symbol': self.safe_symbol(None, market),
                'fundingRate': self.safe_number(entry, 'fundingRate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

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

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

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

    def parse_order(self, order, 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
        #        }
        #     }
        #
        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.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):
        statuses = {
            'triggered': 'open',
            'filled': 'closed',
        }
        return self.safe_string(statuses, status, status)

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

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

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

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

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

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

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

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

    def add_margin(self, symbol: str, amount, 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 self.modify_margin_helper(symbol, amount, 'add', params)

    def reduce_margin(self, symbol: str, amount, 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 self.modify_margin_helper(symbol, amount, 'reduce', params)

    def modify_margin_helper(self, symbol: str, amount, type, params={}) -> MarginModification:
        self.load_markets()
        market = self.market(symbol)
        asset = self.parse_to_int(market['baseId'])
        sz = self.parse_to_int(Precise.string_mul(self.amount_to_precision(symbol, amount), '1000000'))
        if type == 'reduce':
            sz = -sz
        nonce = self.milliseconds()
        updateAction = {
            '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 = {
            'action': updateAction,
            'nonce': nonce,
            'signature': signature,
            # 'vaultAddress': vaultAddress,
        }
        if vaultAddress is not None:
            params = self.omit(params, 'vaultAddress')
            request['vaultAddress'] = vaultAddress
        response = 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, market: Market = None) -> MarginModification:
        #
        #    {
        #        'type': 'default'
        #    }
        #
        return {
            'info': data,
            'symbol': self.safe_symbol(None, market),
            'type': None,
            'marginMode': 'isolated',
            'amount': None,
            'total': None,
            'code': self.safe_string(market, 'settle'),
            'status': None,
            'timestamp': None,
            'datetime': None,
        }

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

    def withdraw(self, code: str, amount, address, tag=None, params={}):
        """
        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()
        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')
        nonce = self.milliseconds()
        payload = {
            'destination': address,
            'usd': str(amount),
            'time': nonce,
        }
        sig = self.build_withdraw_sig(payload)
        request = {
            'action': {
                'chain': 'ArbitrumTestnet' if (isSandboxMode) else 'Arbitrum',
                'payload': payload,
                'type': 'withdraw2',
            },
            'nonce': nonce,
            'signature': sig,
        }
        response = self.privatePostExchange(self.extend(request, params))
        return response

    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 handle_errors(self, code, reason, url, method, headers, body, 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}
