UNPKG

astro-perp-ccxt-dev

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

3,756 lines 166 kB
'use strict';

var hyperliquid$1 = require('./abstract/hyperliquid.js');
var errors = require('./base/errors.js');
var Precise = require('./base/Precise.js');
var number = require('./base/functions/number.js');
var sha3 = require('./static_dependencies/noble-hashes/sha3.js');
var secp256k1 = require('./static_dependencies/noble-curves/secp256k1.js');
var crypto = require('./base/functions/crypto.js');

// ----------------------------------------------------------------------------
//  ---------------------------------------------------------------------------
/**
 * @class hyperliquid
 * @augments Exchange
 */
class hyperliquid extends hyperliquid$1 {
    describe() {
        return this.deepExtend(super.describe(), {
            'id': 'hyperliquid',
            'name': 'Hyperliquid',
            'countries': [],
            'version': 'v1',
            'rateLimit': 50,
            'certified': true,
            'pro': true,
            'dex': true,
            'has': {
                'CORS': undefined,
                'spot': true,
                'margin': false,
                'swap': true,
                'future': true,
                'option': false,
                'addMargin': true,
                'borrowCrossMargin': false,
                'borrowIsolatedMargin': false,
                'cancelAllOrders': false,
                'cancelAllOrdersAfter': true,
                'cancelOrder': true,
                'cancelOrders': true,
                'cancelOrdersForSymbols': true,
                'closeAllPositions': false,
                'closePosition': false,
                'createMarketBuyOrderWithCost': false,
                'createMarketOrderWithCost': false,
                'createMarketSellOrderWithCost': false,
                'createOrder': true,
                'createOrders': true,
                'createOrderWithTakeProfitAndStopLoss': true,
                'createReduceOnlyOrder': true,
                'createStopOrder': true,
                'createTriggerOrder': true,
                'editOrder': true,
                'editOrders': true,
                'fetchAccounts': false,
                'fetchBalance': true,
                'fetchBorrowInterest': false,
                'fetchBorrowRateHistories': false,
                'fetchBorrowRateHistory': false,
                'fetchCanceledAndClosedOrders': true,
                'fetchCanceledOrders': true,
                'fetchClosedOrders': true,
                'fetchCrossBorrowRate': false,
                'fetchCrossBorrowRates': false,
                'fetchCurrencies': true,
                'fetchDepositAddress': false,
                'fetchDepositAddresses': false,
                'fetchDeposits': true,
                'fetchDepositWithdrawFee': 'emulated',
                'fetchDepositWithdrawFees': false,
                'fetchFundingHistory': true,
                'fetchFundingRate': false,
                'fetchFundingRateHistory': true,
                'fetchFundingRates': true,
                'fetchIndexOHLCV': false,
                'fetchIsolatedBorrowRate': false,
                'fetchIsolatedBorrowRates': false,
                'fetchLedger': true,
                'fetchLeverage': false,
                'fetchLeverageTiers': false,
                'fetchLiquidations': false,
                'fetchMarginMode': undefined,
                'fetchMarketLeverageTiers': false,
                'fetchMarkets': true,
                'fetchMarkOHLCV': false,
                'fetchMyLiquidations': false,
                'fetchMyTrades': true,
                'fetchOHLCV': true,
                'fetchOpenInterest': true,
                'fetchOpenInterestHistory': false,
                'fetchOpenInterests': true,
                'fetchOpenOrders': true,
                'fetchOrder': true,
                'fetchOrderBook': true,
                'fetchOrders': true,
                'fetchOrderTrades': false,
                'fetchPosition': true,
                'fetchPositionMode': false,
                'fetchPositions': true,
                'fetchPositionsRisk': false,
                'fetchPremiumIndexOHLCV': false,
                'fetchTicker': 'emulated',
                'fetchTickers': true,
                'fetchTime': false,
                'fetchTrades': true,
                'fetchTradingFee': true,
                'fetchTradingFees': false,
                'fetchTransfer': false,
                'fetchTransfers': false,
                'fetchWithdrawal': false,
                'fetchWithdrawals': true,
                'reduceMargin': true,
                'repayCrossMargin': false,
                'repayIsolatedMargin': false,
                'sandbox': true,
                'setLeverage': true,
                'setMarginMode': true,
                'setPositionMode': false,
                'transfer': true,
                'withdraw': true,
            },
            'timeframes': {
                '1m': '1m',
                '3m': '3m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '8h': '8h',
                '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': {
                            'cost': 20,
                            'byType': {
                                'l2Book': 2,
                                'allMids': 2,
                                'clearinghouseState': 2,
                                'orderStatus': 2,
                                'spotClearinghouseState': 2,
                                'exchangeStatus': 2,
                            },
                        },
                    },
                },
                'private': {
                    'post': {
                        'exchange': 1,
                    },
                },
            },
            'fees': {
                'swap': {
                    'taker': this.parseNumber('0.00045'),
                    'maker': this.parseNumber('0.00015'),
                },
                'spot': {
                    'taker': this.parseNumber('0.0007'),
                    'maker': this.parseNumber('0.0004'),
                },
            },
            'requiredCredentials': {
                'apiKey': false,
                'secret': false,
                'walletAddress': true,
                'privateKey': true,
            },
            'exceptions': {
                'exact': {},
                'broad': {
                    'Price must be divisible by tick size.': errors.InvalidOrder,
                    'Order must have minimum value of $10': errors.InvalidOrder,
                    'Insufficient margin to place order.': errors.InsufficientFunds,
                    'Reduce only order would increase position.': errors.InvalidOrder,
                    'Post only order would have immediately matched,': errors.InvalidOrder,
                    'Order could not immediately match against any resting orders.': errors.InvalidOrder,
                    'Invalid TP/SL price.': errors.InvalidOrder,
                    'No liquidity available for market order.': errors.InvalidOrder,
                    'Order was never placed, already canceled, or filled.': errors.OrderNotFound,
                    'User or API Wallet ': errors.InvalidOrder,
                    'Order has invalid size': errors.InvalidOrder,
                    'Order price cannot be more than 80% away from the reference price': errors.InvalidOrder,
                    'Order has zero size.': errors.InvalidOrder,
                    'Insufficient spot balance asset': errors.InsufficientFunds,
                    'Insufficient balance for withdrawal': errors.InsufficientFunds,
                    'Insufficient balance for token transfer': errors.InsufficientFunds,
                },
            },
            'precisionMode': number.TICK_SIZE,
            'commonCurrencies': {},
            'options': {
                'defaultType': 'swap',
                'sandboxMode': false,
                'defaultSlippage': 0.05,
                'zeroAddress': '0x0000000000000000000000000000000000000000',
            },
            'features': {
                'default': {
                    'sandbox': true,
                    'createOrder': {
                        'marginMode': false,
                        'triggerPrice': false,
                        'triggerPriceType': undefined,
                        'triggerDirection': false,
                        'stopLossPrice': false,
                        'takeProfitPrice': false,
                        'attachedStopLossTakeProfit': {
                            'triggerPriceType': {
                                'last': false,
                                'mark': false,
                                'index': false,
                            },
                            'triggerPrice': true,
                            'type': true,
                            'price': true,
                        },
                        'timeInForce': {
                            'IOC': true,
                            'FOK': false,
                            'PO': true,
                            'GTD': false,
                        },
                        'hedged': false,
                        'trailing': false,
                        'leverage': false,
                        'marketBuyByCost': false,
                        'marketBuyRequiresPrice': false,
                        'selfTradePrevention': false,
                        'iceberg': false,
                    },
                    'createOrders': {
                        'max': 1000,
                    },
                    'fetchMyTrades': {
                        'marginMode': false,
                        'limit': 2000,
                        'daysBack': undefined,
                        'untilDays': undefined,
                        'symbolRequired': true,
                    },
                    'fetchOrder': {
                        'marginMode': false,
                        'trigger': false,
                        'trailing': false,
                        'symbolRequired': true,
                    },
                    'fetchOpenOrders': {
                        'marginMode': false,
                        'limit': 2000,
                        'trigger': false,
                        'trailing': false,
                        'symbolRequired': true,
                    },
                    'fetchOrders': {
                        'marginMode': false,
                        'limit': 2000,
                        'daysBack': undefined,
                        'untilDays': undefined,
                        'trigger': false,
                        'trailing': false,
                        'symbolRequired': true,
                    },
                    'fetchClosedOrders': {
                        'marginMode': false,
                        'limit': 2000,
                        'daysBack': undefined,
                        'daysBackCanceled': undefined,
                        'untilDays': undefined,
                        'trigger': false,
                        'trailing': false,
                        'symbolRequired': true,
                    },
                    'fetchOHLCV': {
                        'limit': 5000,
                    },
                },
                'spot': {
                    'extends': 'default',
                },
                'forPerps': {
                    'extends': 'default',
                    'createOrder': {
                        'stopLossPrice': true,
                        'takeProfitPrice': true,
                        'attachedStopLossTakeProfit': undefined, // todo, in two orders
                    },
                },
                'swap': {
                    'linear': {
                        'extends': 'forPerps',
                    },
                    'inverse': {
                        'extends': 'forPerps',
                    },
                },
                'future': {
                    'linear': {
                        'extends': 'forPerps',
                    },
                    'inverse': {
                        'extends': 'forPerps',
                    },
                },
            },
        });
    }
    setSandboxMode(enabled) {
        super.setSandboxMode(enabled);
        this.options['sandboxMode'] = enabled;
    }
    /**
     * @method
     * @name hyperliquid#fetchCurrencies
     * @description fetches all available currencies on an exchange
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-metadata
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} an associative dictionary of currencies
     */
    async fetchCurrencies(params = {}) {
        const request = {
            'type': 'meta',
        };
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        //     [
        //         {
        //             "universe": [
        //                 {
        //                     "maxLeverage": 50,
        //                     "name": "SOL",
        //                     "onlyIsolated": false,
        //                     "szDecimals": 2
        //                 }
        //             ]
        //         }
        //     ]
        //
        const meta = this.safeList(response, 'universe', []);
        const result = {};
        for (let i = 0; i < meta.length; i++) {
            const data = this.safeDict(meta, i, {});
            const id = i;
            const name = this.safeString(data, 'name');
            const code = this.safeCurrencyCode(name);
            result[code] = this.safeCurrencyStructure({
                'id': id,
                'name': name,
                'code': code,
                'precision': undefined,
                'info': data,
                'active': undefined,
                'deposit': undefined,
                'withdraw': undefined,
                'networks': undefined,
                'fee': undefined,
                'type': 'crypto',
                'limits': {
                    'amount': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'withdraw': {
                        'min': undefined,
                        'max': undefined,
                    },
                },
            });
        }
        return result;
    }
    /**
     * @method
     * @name hyperliquid#fetchMarkets
     * @description retrieves data on all markets for hyperliquid
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object[]} an array of objects representing market data
     */
    async fetchMarkets(params = {}) {
        const rawPromises = [
            this.fetchSwapMarkets(params),
            this.fetchSpotMarkets(params),
        ];
        const promises = await Promise.all(rawPromises);
        const swapMarkets = promises[0];
        const spotMarkets = promises[1];
        return this.arrayConcat(swapMarkets, spotMarkets);
    }
    /**
     * @method
     * @name hyperliquid#fetchSwapMarkets
     * @description retrieves data on all swap markets for hyperliquid
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object[]} an array of objects representing market data
     */
    async fetchSwapMarkets(params = {}) {
        const request = {
            'type': 'metaAndAssetCtxs',
        };
        const response = await this.publicPostInfo(this.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"
        //             }
        //         ]
        //     ]
        //
        //
        const meta = this.safeDict(response, 0, {});
        const universe = this.safeList(meta, 'universe', []);
        const assetCtxs = this.safeList(response, 1, []);
        const result = [];
        for (let i = 0; i < universe.length; i++) {
            const data = this.extend(this.safeDict(universe, i, {}), this.safeDict(assetCtxs, i, {}));
            data['baseId'] = i;
            result.push(data);
        }
        return this.parseMarkets(result);
    }
    /**
     * @method
     * @name hyperliquid#calculatePricePrecision
     * @description Helper function to calculate the Hyperliquid DECIMAL_PLACES price precision
     * @param {float} price the price to use in the calculation
     * @param {int} amountPrecision the amountPrecision to use in the calculation
     * @param {int} maxDecimals the maxDecimals to use in the calculation
     * @returns {int} The calculated price precision
     */
    calculatePricePrecision(price, amountPrecision, maxDecimals) {
        let pricePrecision = 0;
        const priceStr = this.numberToString(price);
        if (priceStr === undefined) {
            return 0;
        }
        const priceSplitted = priceStr.split('.');
        if (Precise["default"].stringEq(priceStr, '0')) {
            // Significant digits is always 5 in this case
            const significantDigits = 5;
            // Integer digits is always 0 in this case (0 doesn't count)
            const integerDigits = 0;
            // Calculate the price precision
            pricePrecision = Math.min(maxDecimals - amountPrecision, significantDigits - integerDigits);
        }
        else if (Precise["default"].stringGt(priceStr, '0') && Precise["default"].stringLt(priceStr, '1')) {
            // Significant digits, always 5 in this case
            const significantDigits = 5;
            // Get the part after the decimal separator
            const decimalPart = this.safeString(priceSplitted, 1, '');
            // Count the number of leading zeros in the decimal part
            let leadingZeros = 0;
            while ((leadingZeros <= decimalPart.length) && (decimalPart[leadingZeros] === '0')) {
                leadingZeros = leadingZeros + 1;
            }
            // Calculate price precision based on leading zeros and significant digits
            pricePrecision = leadingZeros + significantDigits;
            // Calculate the price precision based on maxDecimals - szDecimals and the calculated price precision from the previous step
            pricePrecision = Math.min(maxDecimals - amountPrecision, pricePrecision);
        }
        else {
            // Count the numbers before the decimal separator
            const integerPart = this.safeString(priceSplitted, 0, '');
            // Get significant digits, take the max() of 5 and the integer digits count
            const significantDigits = Math.max(5, integerPart.length);
            // Calculate price precision based on maxDecimals - szDecimals and significantDigits - integerPart.length
            pricePrecision = Math.min(maxDecimals - amountPrecision, significantDigits - integerPart.length);
        }
        return this.parseToInt(pricePrecision);
    }
    /**
     * @method
     * @name hyperliquid#fetchSpotMarkets
     * @description retrieves data on all spot markets for hyperliquid
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object[]} an array of objects representing market data
     */
    async fetchSpotMarkets(params = {}) {
        const request = {
            'type': 'spotMetaAndAssetCtxs',
        };
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        // [
        //     {
        //         "tokens": [
        //             {
        //                 "name": "USDC",
        //                 "szDecimals": 8,
        //                 "weiDecimals" 8,
        //                 "index": 0,
        //                 "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054",
        //                 "isCanonical": true,
        //                 "evmContract":null,
        //                 "fullName":null
        //             },
        //             {
        //                 "name": "PURR",
        //                 "szDecimals": 0,
        //                 "weiDecimals": 5,
        //                 "index": 1,
        //                 "tokenId": "0xc1fb593aeffbeb02f85e0308e9956a90",
        //                 "isCanonical": true,
        //                 "evmContract":null,
        //                 "fullName":null
        //             }
        //         ],
        //         "universe": [
        //             {
        //                 "name": "PURR/USDC",
        //                 "tokens": [1, 0],
        //                 "index": 0,
        //                 "isCanonical": true
        //             }
        //         ]
        //     },
        //     [
        //         {
        //             "dayNtlVlm":"8906.0",
        //             "markPx":"0.14",
        //             "midPx":"0.209265",
        //             "prevDayPx":"0.20432"
        //         }
        //     ]
        // ]
        //
        const first = this.safeDict(response, 0, {});
        const second = this.safeList(response, 1, []);
        const meta = this.safeList(first, 'universe', []);
        const tokens = this.safeList(first, 'tokens', []);
        const markets = [];
        for (let i = 0; i < meta.length; i++) {
            const market = this.safeDict(meta, i, {});
            const index = this.safeInteger(market, 'index');
            const extraData = this.safeDict(second, index, {});
            const marketName = this.safeString(market, 'name');
            // if (marketName.indexOf ('/') < 0) {
            //     // there are some weird spot markets in testnet, eg @2
            //     continue;
            // }
            // const marketParts = marketName.split ('/');
            // const baseName = this.safeString (marketParts, 0);
            // const quoteId = this.safeString (marketParts, 1);
            const fees = this.safeDict(this.fees, 'spot', {});
            const taker = this.safeNumber(fees, 'taker');
            const maker = this.safeNumber(fees, 'maker');
            const tokensPos = this.safeList(market, 'tokens', []);
            const baseTokenPos = this.safeInteger(tokensPos, 0);
            const quoteTokenPos = this.safeInteger(tokensPos, 1);
            const baseTokenInfo = this.safeDict(tokens, baseTokenPos, {});
            const quoteTokenInfo = this.safeDict(tokens, quoteTokenPos, {});
            const baseName = this.safeString(baseTokenInfo, 'name');
            const quoteId = this.safeString(quoteTokenInfo, 'name');
            const base = this.safeCurrencyCode(baseName);
            const quote = this.safeCurrencyCode(quoteId);
            const symbol = base + '/' + quote;
            const innerBaseTokenInfo = this.safeDict(baseTokenInfo, 'spec', baseTokenInfo);
            // const innerQuoteTokenInfo = this.safeDict (quoteTokenInfo, 'spec', quoteTokenInfo);
            const amountPrecisionStr = this.safeString(innerBaseTokenInfo, 'szDecimals');
            const amountPrecision = parseInt(amountPrecisionStr);
            const price = this.safeNumber(extraData, 'midPx');
            let pricePrecision = 0;
            if (price !== undefined) {
                pricePrecision = this.calculatePricePrecision(price, amountPrecision, 8);
            }
            const pricePrecisionStr = this.numberToString(pricePrecision);
            // const quotePrecision = this.parseNumber (this.parsePrecision (this.safeString (innerQuoteTokenInfo, 'szDecimals')));
            const baseId = this.numberToString(index + 10000);
            markets.push(this.safeMarketStructure({
                'id': marketName,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': undefined,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': undefined,
                'type': 'spot',
                'spot': true,
                'subType': undefined,
                'margin': undefined,
                'swap': false,
                'future': false,
                'option': false,
                'active': true,
                'contract': false,
                'linear': undefined,
                'inverse': undefined,
                'taker': taker,
                'maker': maker,
                'contractSize': undefined,
                'expiry': undefined,
                'expiryDatetime': undefined,
                'strike': undefined,
                'optionType': undefined,
                'precision': {
                    'amount': this.parseNumber(this.parsePrecision(amountPrecisionStr)),
                    'price': this.parseNumber(this.parsePrecision(pricePrecisionStr)),
                },
                'limits': {
                    'leverage': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'amount': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'price': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'cost': {
                        'min': this.parseNumber('10'),
                        'max': undefined,
                    },
                },
                'created': undefined,
                'info': this.extend(extraData, market),
            }));
        }
        return markets;
    }
    parseMarket(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"
        //     }
        //
        const quoteId = 'USDC';
        const baseName = this.safeString(market, 'name');
        const base = this.safeCurrencyCode(baseName);
        const quote = this.safeCurrencyCode(quoteId);
        const baseId = this.safeString(market, 'baseId');
        const settleId = 'USDC';
        const settle = this.safeCurrencyCode(settleId);
        let symbol = base + '/' + quote;
        const contract = true;
        const swap = true;
        {
            {
                symbol = symbol + ':' + settle;
            }
        }
        const fees = this.safeDict(this.fees, 'swap', {});
        const taker = this.safeNumber(fees, 'taker');
        const maker = this.safeNumber(fees, 'maker');
        const amountPrecisionStr = this.safeString(market, 'szDecimals');
        const amountPrecision = parseInt(amountPrecisionStr);
        const price = this.safeNumber(market, 'markPx', 0);
        let pricePrecision = 0;
        if (price !== undefined) {
            pricePrecision = this.calculatePricePrecision(price, amountPrecision, 6);
        }
        const pricePrecisionStr = this.numberToString(pricePrecision);
        const isDelisted = this.safeBool(market, 'isDelisted');
        let active = true;
        if (isDelisted !== undefined) {
            active = !isDelisted;
        }
        return this.safeMarketStructure({
            'id': baseId,
            'symbol': symbol,
            'base': base,
            'quote': quote,
            'settle': settle,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': settleId,
            'type': 'swap',
            'spot': false,
            'margin': undefined,
            'swap': swap,
            'future': false,
            'option': false,
            'active': active,
            'contract': contract,
            'linear': true,
            'inverse': false,
            'taker': taker,
            'maker': maker,
            'contractSize': this.parseNumber('1'),
            'expiry': undefined,
            'expiryDatetime': undefined,
            'strike': undefined,
            'optionType': undefined,
            'precision': {
                'amount': this.parseNumber(this.parsePrecision(amountPrecisionStr)),
                'price': this.parseNumber(this.parsePrecision(pricePrecisionStr)),
            },
            'limits': {
                'leverage': {
                    'min': undefined,
                    'max': this.safeInteger(market, 'maxLeverage'),
                },
                'amount': {
                    'min': undefined,
                    'max': undefined,
                },
                'price': {
                    'min': undefined,
                    'max': undefined,
                },
                'cost': {
                    'min': this.parseNumber('10'),
                    'max': undefined,
                },
            },
            'created': undefined,
            'info': market,
        });
    }
    /**
     * @method
     * @name hyperliquid#fetchBalance
     * @description 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/spot#retrieve-a-users-token-balances
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @param {string} [params.type] wallet type, ['spot', 'swap'], defaults to swap
     * @param {string} [params.marginMode] 'cross' or 'isolated', for margin trading, uses this.options.defaultMarginMode if not passed, defaults to undefined/None/null
     * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
     */
    async fetchBalance(params = {}) {
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchBalance', params);
        let type = undefined;
        [type, params] = this.handleMarketTypeAndParams('fetchBalance', undefined, params);
        let marginMode = undefined;
        [marginMode, params] = this.handleMarginModeAndParams('fetchBalance', params);
        const isSpot = (type === 'spot');
        const reqType = (isSpot) ? 'spotClearinghouseState' : 'clearinghouseState';
        const request = {
            'type': reqType,
            'user': userAddress,
        };
        const response = await this.publicPostInfo(this.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"
        //            }
        //     }
        //
        const balances = this.safeList(response, 'balances');
        if (balances !== undefined) {
            const spotBalances = { 'info': response };
            for (let i = 0; i < balances.length; i++) {
                const balance = balances[i];
                const code = this.safeCurrencyCode(this.safeString(balance, 'coin'));
                const account = this.account();
                const total = this.safeString(balance, 'total');
                const used = this.safeString(balance, 'hold');
                account['total'] = total;
                account['used'] = used;
                spotBalances[code] = account;
            }
            return this.safeBalance(spotBalances);
        }
        const data = this.safeDict(response, 'marginSummary', {});
        const usdcBalance = {
            'total': this.safeNumber(data, 'accountValue'),
        };
        if ((marginMode !== undefined) && (marginMode === 'isolated')) {
            usdcBalance['free'] = this.safeNumber(response, 'withdrawable');
        }
        else {
            usdcBalance['used'] = this.safeNumber(data, 'totalMarginUsed');
        }
        const result = {
            'info': response,
            'USDC': usdcBalance,
        };
        const timestamp = this.safeInteger(response, 'time');
        result['timestamp'] = timestamp;
        result['datetime'] = this.iso8601(timestamp);
        return this.safeBalance(result);
    }
    /**
     * @method
     * @name hyperliquid#fetchOrderBook
     * @description 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#l2-book-snapshot
     * @param {string} symbol unified symbol of the market to fetch the order book for
     * @param {int} [limit] the maximum amount of order book entries to return
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
     */
    async fetchOrderBook(symbol, limit = undefined, params = {}) {
        await this.loadMarkets();
        const market = this.market(symbol);
        const request = {
            'type': 'l2Book',
            'coin': market['swap'] ? market['base'] : market['id'],
        };
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        //     {
        //         "coin": "ETH",
        //         "levels": [
        //             [
        //                 {
        //                     "n": "2",
        //                     "px": "2216.2",
        //                     "sz": "74.0637"
        //                 }
        //             ],
        //             [
        //                 {
        //                     "n": "2",
        //                     "px": "2216.5",
        //                     "sz": "70.5893"
        //                 }
        //             ]
        //         ],
        //         "time": "1704290104840"
        //     }
        //
        const data = this.safeList(response, 'levels', []);
        const result = {
            'bids': this.safeList(data, 0, []),
            'asks': this.safeList(data, 1, []),
        };
        const timestamp = this.safeInteger(response, 'time');
        return this.parseOrderBook(result, market['symbol'], timestamp, 'bids', 'asks', 'px', 'sz');
    }
    /**
     * @method
     * @name hyperliquid#fetchTickers
     * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
     * @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.type] 'spot' or 'swap', by default fetches both
     * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
     */
    async fetchTickers(symbols = undefined, params = {}) {
        await this.loadMarkets();
        symbols = this.marketSymbols(symbols);
        // at this stage, to get tickers data, we use fetchMarkets endpoints
        let response = [];
        const type = this.safeString(params, 'type');
        params = this.omit(params, 'type');
        if (type === 'spot') {
            response = await this.fetchSpotMarkets(params);
        }
        else if (type === 'swap') {
            response = await this.fetchSwapMarkets(params);
        }
        else {
            response = await this.fetchMarkets(params);
        }
        // same response as under "fetchMarkets"
        const result = {};
        for (let i = 0; i < response.length; i++) {
            const market = response[i];
            const info = market['info'];
            const ticker = this.parseTicker(info, market);
            const symbol = this.safeString(ticker, 'symbol');
            result[symbol] = ticker;
        }
        return this.filterByArrayTickers(result, 'symbol', symbols);
    }
    /**
     * @method
     * @name hyperliquid#fetchFundingRates
     * @description retrieves data on all swap markets for hyperliquid
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
     * @param {string[]} [symbols] list of unified market symbols
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object[]} an array of objects representing market data
     */
    async fetchFundingRates(symbols = undefined, params = {}) {
        const request = {
            'type': 'metaAndAssetCtxs',
        };
        const response = await this.publicPostInfo(this.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"
        //             }
        //         ]
        //     ]
        //
        //
        const meta = this.safeDict(response, 0, {});
        const universe = this.safeList(meta, 'universe', []);
        const assetCtxs = this.safeList(response, 1, []);
        const result = [];
        for (let i = 0; i < universe.length; i++) {
            const data = this.extend(this.safeDict(universe, i, {}), this.safeDict(assetCtxs, i, {}));
            result.push(data);
        }
        return this.parseFundingRates(result, symbols);
    }
    parseFundingRate(info, market = undefined) {
        //
        //     {
        //         "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"
        //     }
        //
        const base = this.safeString(info, 'name');
        const marketId = this.coinToMarketId(base);
        const symbol = this.safeSymbol(marketId, market);
        const funding = this.safeNumber(info, 'funding');
        const markPx = this.safeNumber(info, 'markPx');
        const oraclePx = this.safeNumber(info, 'oraclePx');
        const fundingTimestamp = (Math.floor(this.milliseconds() / 60 / 60 / 1000) + 1) * 60 * 60 * 1000;
        return {
            'info': info,
            'symbol': symbol,
            'markPrice': markPx,
            'indexPrice': oraclePx,
            'interestRate': undefined,
            'estimatedSettlePrice': undefined,
            'timestamp': undefined,
            'datetime': undefined,
            'fundingRate': funding,
            'fundingTimestamp': fundingTimestamp,
            'fundingDatetime': this.iso8601(fundingTimestamp),
            'nextFundingRate': undefined,
            'nextFundingTimestamp': undefined,
            'nextFundingDatetime': undefined,
            'previousFundingRate': undefined,
            'previousFundingTimestamp': undefined,
            'previousFundingDatetime': undefined,
            'interval': '1h',
        };
    }
    parseTicker(ticker, market = undefined) {
        //
        //     {
        //         "prevDayPx": "3400.5",
        //         "dayNtlVlm": "511297257.47936022",
        //         "markPx": "3464.7",
        //         "midPx": "3465.05",
        //         "oraclePx": "3460.1", // only in swap
        //         "openInterest": "64638.1108", // only in swap
        //         "premium": "0.00141614", // only in swap
        //         "funding": "0.00008727", // only in swap
        //         "impactPxs": [ "3465.0", "3465.1" ], // only in swap
        //         "coin": "PURR", // only in spot
        //         "circulatingSupply": "998949190.03400207", // only in spot
        //     },
        //
        const bidAsk = this.safeList(ticker, 'impactPxs');
        return this.safeTicker({
            'symbol': market['symbol'],
            'timestamp': undefined,
            'datetime': undefined,
            'previousClose': this.safeNumber(ticker, 'prevDayPx'),
            'close': this.safeNumber(ticker, 'midPx'),
            'bid': this.safeNumber(bidAsk, 0),
            'ask': this.safeNumber(bidAsk, 1),
            'quoteVolume': this.safeNumber(ticker, 'dayNtlVlm'),
            'info': ticker,
        }, market);
    }
    /**
     * @method
     * @name hyperliquid#fetchOHLCV
     * @description 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#candle-snapshot
     * @param {string} symbol unified symbol of the market to fetch OHLCV data for
     * @param {string} 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 {object} [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 as timestamp, open, high, low, close, volume
     */
    async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        const market = this.market(symbol);
        const until = this.safeInteger(params, 'until', this.milliseconds());
        let useTail = since === undefined;
        const originalSince = since;
        if (since === undefined) {
            if (limit !== undefined) {
                // optimization if limit is provided
                const timeframeInMilliseconds = this.parseTimeframe(timeframe) * 1000;
                since = this.sum(until, timeframeInMilliseconds * limit * -1);
                useTail = false;
            }
            else {
                since = 0;
            }
        }
        params = this.omit(params, ['until']);
        const request = {
            'type': 'candleSnapshot',
            'req': {
                'coin': market['swap'] ? market['base'] : market['id'],
                'interval': this.safeString(this.timeframes, timeframe, timeframe),
                'startTime': since,
                'endTime': until,
            },
        };
        const response = await this.publicPostInfo(this.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 this.parseOHLCVs(response, market, timeframe, originalSince, limit, useTail);
    }
    parseOHLCV(ohlcv, market = undefined) {
        //
        //     {
        //         "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 [
            this.safeInteger(ohlcv, 't'),
            this.safeNumber(ohlcv, 'o'),
            this.safeNumber(ohlcv, 'h'),
            this.safeNumber(ohlcv, 'l'),
            this.safeNumber(ohlcv, 'c'),
            this.safeNumber(ohlcv, 'v'),
        ];
    }
    /**
     * @method
     * @name hyperliquid#fetchTrades
     * @description 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 {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch trades for
     * @param {int} [limit] the maximum number of trades structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {int} [params.until] timestamp in ms of the latest trade
     * @param {string} [params.address] wallet address that made trades
     * @param {string} [params.user] wallet address that made trades
     * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
     */
    async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchTrades', params);
        await this.loadMarkets();
        const market = this.safeMarket(symbol);
        const request = {
            'user': userAddress,
        };
        if (since !== undefined) {
            request['type'] = 'userFillsByTime';
            request['startTime'] = since;
        }
        else {
            request['type'] = 'userFills';
        }
        const until = this.safeInteger(params, 'until');
        params = this.omit(params, 'until');
        if (until !== undefined) {
            request['endTime'] = until;
        }
        const response = await this.publicPostInfo(this.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 this.parseTrades(response, market, since, limit);
    }
    amountToPrecision(symbol, amount) {
        const market = this.market(symbol);
        return this.decimalToPrecision(amount, number.ROUND, market['precision']['amount'], this.precisionMode, this.paddingMode);
    }
    priceToPrecision(symbol, price) {
        const market = this.market(symbol);
        const priceStr = this.numberToString(price);
        const integerPart = priceStr.split('.')[0];
        const significantDigits = Math.max(5, integerPart.length);
        const result = this.decimalToPrecision(price, number.ROUND, significantDigits, number.SIGNIFICANT_DIGITS, this.paddingMode);
        const maxDecimals = market['spot'] ? 8 : 6;
        const subtractedValue = maxDecimals - this.precisionFromString(this.safeString(market['precision'], 'amount'));
        return this.decimalToPrecision(result, number.ROUND, subtractedValue, number.DECIMAL_PLACES, this.paddingMode);
    }
    hashMessage(message) {
        return '0x' + this.hash(message, sha3.keccak_256, 'hex');
    }
    signHash(hash, privateKey) {
        const signature = crypto.ecdsa(hash.slice(-64), privateKey.slice(-64), secp256k1.secp256k1, undefined);
        return {
            'r': '0x' + signature['r'],
            's': '0x' + signature['s'],
            'v': this.sum(27, signature['v']),
        };
    }
    signMessage(message, privateKey) {
        return this.signHash(this.hashMessage(message), privateKey.slice(-64));
    }
    constructPhantomAgent(hash, isTestnet = true) {
        const source = (isTestnet) ? 'b' : 'a';
        return {
            'source': source,
            'connectionId': hash,
        };
    }
    actionHash(action, vaultAddress, nonce) {
        const dataBinary = this.packb(action);
        const dataHex = this.binaryToBase16(dataBinary);
        let data = dataHex;
        data += '00000' + this.intToBase16(nonce);
        if (vaultAddress === undefined) {
            data += '00';
        }
        else {
            data += '01';
            data += vaultAddress;
        }
        return this.hash(this.base16ToBinary(data), sha3.keccak_256, 'binary');
    }
    signL1Action(action, nonce, vaultAdress = undefined) {
        const hash = this.actionHash(action, vaultAdress, nonce);
        const isTestnet = this.safeBool(this.options, 'sandboxMode', false);
        const phantomAgent = this.constructPhantomAgent(hash, isTestnet);
        // const data: Dict = {
        //     'domain': {
        //         'chainId': 1337,
        //         'name': 'Exchange',
        //         'verifyingContract': '0x0000000000000000000000000000000000000000',
        //         'version': '1',
        //     },
        //     'types': {
        //         'Agent': [
        //             { 'name': 'source', 'type': 'string' },
        //             { 'name': 'connectionId', 'type': 'bytes32' },
        //         ],
        //         'EIP712Domain': [
        //             { 'name': 'name', 'type': 'string' },
        //             { 'name': 'version', 'type': 'string' },
        //             { 'name': 'chainId', 'type': 'uint256' },
        //             { 'name': 'verifyingContract', 'type': 'address' },
        //         ],
        //     },
        //     'primaryType': 'Agent',
        //     'message': phantomAgent,
        // };
        const zeroAddress = this.safeString(this.options, 'zeroAddress');
        const chainId = 1337; // check this out
        const domain = {
            'chainId': chainId,
            'name': 'Exchange',
            'verifyingContract': zeroAddress,
            'version': '1',
        };
        const messageTypes = {
            'Agent': [
                { 'name': 'source', 'type': 'string' },
                { 'name': 'connectionId', 'type': 'bytes32' },
            ],
        };
        const msg = this.ethEncodeStructuredData(domain, messageTypes, phantomAgent);
        const signature = this.signMessage(msg, this.privateKey);
        return signature;
    }
    signUserSignedAction(messageTypes, message) {
        const zeroAddress = this.safeString(this.options, 'zeroAddress');
        const chainId = 421614; // check this out
        const domain = {
            'chainId': chainId,
            'name': 'HyperliquidSignTransaction',
            'verifyingContract': zeroAddress,
            'version': '1',
        };
        const msg = this.ethEncodeStructuredData(domain, messageTypes, message);
        const signature = this.signMessage(msg, this.privateKey);
        return signature;
    }
    buildUsdSendSig(message) {
        const messageTypes = {
            'HyperliquidTransaction:UsdSend': [
                { 'name': 'hyperliquidChain', 'type': 'string' },
                { 'name': 'destination', 'type': 'string' },
                { 'name': 'amount', 'type': 'string' },
                { 'name': 'time', 'type': 'uint64' },
            ],
        };
        return this.signUserSignedAction(messageTypes, message);
    }
    buildUsdClassSendSig(message) {
        const messageTypes = {
            'HyperliquidTransaction:UsdClassTransfer': [
                { 'name': 'hyperliquidChain', 'type': 'string' },
                { 'name': 'amount', 'type': 'string' },
                { 'name': 'toPerp', 'type': 'bool' },
                { 'name': 'nonce', 'type': 'uint64' },
            ],
        };
        return this.signUserSignedAction(messageTypes, message);
    }
    buildWithdrawSig(message) {
        const messageTypes = {
            'HyperliquidTransaction:Withdraw': [
                { 'name': 'hyperliquidChain', 'type': 'string' },
                { 'name': 'destination', 'type': 'string' },
                { 'name': 'amount', 'type': 'string' },
                { 'name': 'time', 'type': 'uint64' },
            ],
        };
        return this.signUserSignedAction(messageTypes, message);
    }
    /**
     * @method
     * @name hyperliquid#createOrder
     * @description create a trade order
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
     * @param {string} symbol unified symbol of the market to create an order in
     * @param {string} type 'market' or 'limit'
     * @param {string} side 'buy' or 'sell'
     * @param {float} amount how much of currency you want to trade in units of base currency
     * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [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 {string} [params.clientOrderId] client order id, (optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
     * @param {string} [params.slippage] the slippage for market order
     * @param {string} [params.vaultAddress] the vault address for order
     * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets();
        const [order, globalParams] = this.parseCreateEditOrderArgs(undefined, symbol, type, side, amount, price, params);
        const orders = await this.createOrders([order], globalParams);
        return orders[0];
    }
    /**
     * @method
     * @name hyperliquid#createOrders
     * @description 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
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async createOrders(orders, params = {}) {
        await this.loadMarkets();
        const request = this.createOrdersRequest(orders, params);
        const response = await this.privatePostExchange(request);
        //
        //     {
        //         "status": "ok",
        //         "response": {
        //             "type": "order",
        //             "data": {
        //                 "statuses": [
        //                     {
        //                         "resting": {
        //                             "oid": 5063830287
        //                         }
        //                     }
        //                 ]
        //             }
        //         }
        //     }
        //
        const responseObj = this.safeDict(response, 'response', {});
        const data = this.safeDict(responseObj, 'data', {});
        const statuses = this.safeList(data, 'statuses', []);
        return this.parseOrders(statuses, undefined);
    }
    createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
        const market = this.market(symbol);
        type = type.toUpperCase();
        side = side.toUpperCase();
        const isMarket = (type === 'MARKET');
        const isBuy = (side === 'BUY');
        const clientOrderId = this.safeString2(params, 'clientOrderId', 'client_id');
        const slippage = this.safeString(params, 'slippage');
        let defaultTimeInForce = (isMarket) ? 'ioc' : 'gtc';
        const postOnly = this.safeBool(params, 'postOnly', false);
        if (postOnly) {
            defaultTimeInForce = 'alo';
        }
        let timeInForce = this.safeStringLower(params, 'timeInForce', defaultTimeInForce);
        timeInForce = this.capitalize(timeInForce);
        let triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
        const stopLossPrice = this.safeString(params, 'stopLossPrice', triggerPrice);
        const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
        const isTrigger = (stopLossPrice || takeProfitPrice);
        let px = undefined;
        if (isMarket) {
            if (price === undefined) {
                throw new errors.ArgumentsRequired(this.id + '  market orders require price to calculate the max slippage price. Default slippage can be set in options (default is 5%).');
            }
            px = (isBuy) ? Precise["default"].stringMul(price, Precise["default"].stringAdd('1', slippage)) : Precise["default"].stringMul(price, Precise["default"].stringSub('1', slippage));
            px = this.priceToPrecision(symbol, px); // round after adding slippage
        }
        else {
            px = this.priceToPrecision(symbol, price);
        }
        const sz = this.amountToPrecision(symbol, amount);
        const reduceOnly = this.safeBool(params, 'reduceOnly', false);
        const orderType = {};
        if (isTrigger) {
            let isTp = false;
            if (takeProfitPrice !== undefined) {
                triggerPrice = this.priceToPrecision(symbol, takeProfitPrice);
                isTp = true;
            }
            else {
                triggerPrice = this.priceToPrecision(symbol, stopLossPrice);
            }
            orderType['trigger'] = {
                'isMarket': isMarket,
                'triggerPx': triggerPrice,
                'tpsl': (isTp) ? 'tp' : 'sl',
            };
        }
        else {
            orderType['limit'] = {
                'tif': timeInForce,
            };
        }
        params = this.omit(params, ['clientOrderId', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce', 'client_id', 'reduceOnly', 'postOnly']);
        const orderObj = {
            'a': this.parseToInt(market['baseId']),
            'b': isBuy,
            'p': px,
            's': sz,
            'r': reduceOnly,
            't': orderType,
            // 'c': clientOrderId,
        };
        if (clientOrderId !== undefined) {
            orderObj['c'] = clientOrderId;
        }
        return orderObj;
    }
    createOrdersRequest(orders, params = {}) {
        /**
         * @method
         * @name hyperliquid#createOrdersRequest
         * @description 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 {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
         */
        this.checkRequiredCredentials();
        let defaultSlippage = this.safeString(this.options, 'defaultSlippage');
        defaultSlippage = this.safeString(params, 'slippage', defaultSlippage);
        let hasClientOrderId = false;
        for (let i = 0; i < orders.length; i++) {
            const rawOrder = orders[i];
            const orderParams = this.safeDict(rawOrder, 'params', {});
            const clientOrderId = this.safeString2(orderParams, 'clientOrderId', 'client_id');
            if (clientOrderId !== undefined) {
                hasClientOrderId = true;
            }
        }
        if (hasClientOrderId) {
            for (let i = 0; i < orders.length; i++) {
                const rawOrder = orders[i];
                const orderParams = this.safeDict(rawOrder, 'params', {});
                const clientOrderId = this.safeString2(orderParams, 'clientOrderId', 'client_id');
                if (clientOrderId === undefined) {
                    throw new errors.ArgumentsRequired(this.id + ' createOrders() all orders must have clientOrderId if at least one has a clientOrderId');
                }
            }
        }
        params = this.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce']);
        const nonce = this.milliseconds();
        const orderReq = [];
        let grouping = 'na';
        for (let i = 0; i < orders.length; i++) {
            const rawOrder = orders[i];
            const marketId = this.safeString(rawOrder, 'symbol');
            const market = this.market(marketId);
            const symbol = market['symbol'];
            const type = this.safeStringUpper(rawOrder, 'type');
            const side = this.safeStringUpper(rawOrder, 'side');
            const amount = this.safeString(rawOrder, 'amount');
            const price = this.safeString(rawOrder, 'price');
            let orderParams = this.safeDict(rawOrder, 'params', {});
            const slippage = this.safeString(orderParams, 'slippage', defaultSlippage);
            orderParams['slippage'] = slippage;
            const stopLoss = this.safeValue(orderParams, 'stopLoss');
            const takeProfit = this.safeValue(orderParams, 'takeProfit');
            const isTrigger = (stopLoss || takeProfit);
            orderParams = this.omit(orderParams, ['stopLoss', 'takeProfit']);
            const mainOrderObj = this.createOrderRequest(symbol, type, side, amount, price, orderParams);
            orderReq.push(mainOrderObj);
            if (isTrigger) {
                // grouping opposed orders for sl/tp
                const stopLossOrderTriggerPrice = this.safeStringN(stopLoss, ['triggerPrice', 'stopPrice']);
                const stopLossOrderType = this.safeString(stopLoss, 'type');
                const stopLossOrderLimitPrice = this.safeStringN(stopLoss, ['price', 'stopLossPrice'], stopLossOrderTriggerPrice);
                const takeProfitOrderTriggerPrice = this.safeStringN(takeProfit, ['triggerPrice', 'stopPrice']);
                const takeProfitOrderType = this.safeString(takeProfit, 'type');
                const takeProfitOrderLimitPrice = this.safeStringN(takeProfit, ['price', 'takeProfitPrice'], takeProfitOrderTriggerPrice);
                grouping = 'normalTpsl';
                orderParams = this.omit(orderParams, ['stopLoss', 'takeProfit']);
                let triggerOrderSide = '';
                if (side === 'BUY') {
                    triggerOrderSide = 'sell';
                }
                else {
                    triggerOrderSide = 'buy';
                }
                if (takeProfit !== undefined) {
                    const orderObj = this.createOrderRequest(symbol, takeProfitOrderType, triggerOrderSide, amount, takeProfitOrderLimitPrice, this.extend(orderParams, {
                        'takeProfitPrice': takeProfitOrderTriggerPrice,
                        'reduceOnly': true,
                    }));
                    orderReq.push(orderObj);
                }
                if (stopLoss !== undefined) {
                    const orderObj = this.createOrderRequest(symbol, stopLossOrderType, triggerOrderSide, amount, stopLossOrderLimitPrice, this.extend(orderParams, {
                        'stopLossPrice': stopLossOrderTriggerPrice,
                        'reduceOnly': true,
                    }));
                    orderReq.push(orderObj);
                }
            }
        }
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'createOrder', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        const orderAction = {
            'type': 'order',
            'orders': orderReq,
            'grouping': grouping,
            // 'brokerCode': 1, // cant
        };
        if (vaultAddress === undefined) {
            orderAction['brokerCode'] = 1;
        }
        const signature = this.signL1Action(orderAction, nonce, vaultAddress);
        const request = {
            'action': orderAction,
            'nonce': nonce,
            'signature': signature,
            // 'vaultAddress': vaultAddress,
        };
        if (vaultAddress !== undefined) {
            params = this.omit(params, 'vaultAddress');
            request['vaultAddress'] = vaultAddress;
        }
        return request;
    }
    /**
     * @method
     * @name hyperliquid#cancelOrder
     * @description 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 {string} id order id
     * @param {string} symbol unified symbol of the market the order was made in
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.clientOrderId] client order id, (optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
     * @param {string} [params.vaultAddress] the vault address for order
     * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async cancelOrder(id, symbol = undefined, params = {}) {
        const orders = await this.cancelOrders([id], symbol, params);
        return this.safeDict(orders, 0);
    }
    /**
     * @method
     * @name hyperliquid#cancelOrders
     * @description 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 {string[]} ids order ids
     * @param {string} [symbol] unified market symbol
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string|string[]} [params.clientOrderId] client order ids, (optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
     * @param {string} [params.vaultAddress] the vault address
     * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async cancelOrders(ids, symbol = undefined, params = {}) {
        this.checkRequiredCredentials();
        if (symbol === undefined) {
            throw new errors.ArgumentsRequired(this.id + ' cancelOrders() requires a symbol argument');
        }
        await this.loadMarkets();
        const market = this.market(symbol);
        let clientOrderId = this.safeValue2(params, 'clientOrderId', 'client_id');
        params = this.omit(params, ['clientOrderId', 'client_id']);
        const nonce = this.milliseconds();
        const request = {
            'nonce': nonce,
            // 'vaultAddress': vaultAddress,
        };
        const cancelReq = [];
        const cancelAction = {
            'type': '',
            'cancels': [],
        };
        const baseId = this.parseToNumeric(market['baseId']);
        if (clientOrderId !== undefined) {
            if (!Array.isArray(clientOrderId)) {
                clientOrderId = [clientOrderId];
            }
            cancelAction['type'] = 'cancelByCloid';
            for (let i = 0; i < clientOrderId.length; i++) {
                cancelReq.push({
                    'asset': baseId,
                    'cloid': clientOrderId[i],
                });
            }
        }
        else {
            cancelAction['type'] = 'cancel';
            for (let i = 0; i < ids.length; i++) {
                cancelReq.push({
                    'a': baseId,
                    'o': this.parseToNumeric(ids[i]),
                });
            }
        }
        cancelAction['cancels'] = cancelReq;
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'cancelOrders', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        const signature = this.signL1Action(cancelAction, nonce, vaultAddress);
        request['action'] = cancelAction;
        request['signature'] = signature;
        if (vaultAddress !== undefined) {
            params = this.omit(params, 'vaultAddress');
            request['vaultAddress'] = vaultAddress;
        }
        const response = await this.privatePostExchange(request);
        //
        //     {
        //         "status":"ok",
        //         "response":{
        //             "type":"cancel",
        //             "data":{
        //                 "statuses":[
        //                     "success"
        //                 ]
        //             }
        //         }
        //     }
        //
        const innerResponse = this.safeDict(response, 'response');
        const data = this.safeDict(innerResponse, 'data');
        const statuses = this.safeList(data, 'statuses');
        const orders = [];
        for (let i = 0; i < statuses.length; i++) {
            const status = statuses[i];
            orders.push(this.safeOrder({
                'info': status,
                'status': status,
            }));
        }
        return orders;
    }
    /**
     * @method
     * @name hyperliquid#cancelOrdersForSymbols
     * @description cancel multiple orders for multiple symbols
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
     * @param {CancellationRequest[]} orders each order should contain the parameters required by cancelOrder namely id and symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.vaultAddress] the vault address
     * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async cancelOrdersForSymbols(orders, params = {}) {
        this.checkRequiredCredentials();
        await this.loadMarkets();
        const nonce = this.milliseconds();
        const request = {
            'nonce': nonce,
            // 'vaultAddress': vaultAddress,
        };
        const cancelReq = [];
        const cancelAction = {
            'type': '',
            'cancels': [],
        };
        let cancelByCloid = false;
        for (let i = 0; i < orders.length; i++) {
            const order = orders[i];
            const clientOrderId = this.safeString(order, 'clientOrderId');
            if (clientOrderId !== undefined) {
                cancelByCloid = true;
            }
            const id = this.safeString(order, 'id');
            const symbol = this.safeString(order, 'symbol');
            if (symbol === undefined) {
                throw new errors.ArgumentsRequired(this.id + ' cancelOrdersForSymbols() requires a symbol argument in each order');
            }
            if (id !== undefined && cancelByCloid) {
                throw new errors.BadRequest(this.id + ' cancelOrdersForSymbols() all orders must have either id or clientOrderId');
            }
            const assetKey = cancelByCloid ? 'asset' : 'a';
            const idKey = cancelByCloid ? 'cloid' : 'o';
            const market = this.market(symbol);
            const cancelObj = {};
            cancelObj[assetKey] = this.parseToNumeric(market['baseId']);
            cancelObj[idKey] = cancelByCloid ? clientOrderId : this.parseToNumeric(id);
            cancelReq.push(cancelObj);
        }
        cancelAction['type'] = cancelByCloid ? 'cancelByCloid' : 'cancel';
        cancelAction['cancels'] = cancelReq;
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'cancelOrdersForSymbols', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        const signature = this.signL1Action(cancelAction, nonce, vaultAddress);
        request['action'] = cancelAction;
        request['signature'] = signature;
        if (vaultAddress !== undefined) {
            params = this.omit(params, 'vaultAddress');
            request['vaultAddress'] = vaultAddress;
        }
        const response = await this.privatePostExchange(request);
        //
        //     {
        //         "status":"ok",
        //         "response":{
        //             "type":"cancel",
        //             "data":{
        //                 "statuses":[
        //                     "success"
        //                 ]
        //             }
        //         }
        //     }
        //
        return response;
    }
    /**
     * @method
     * @name hyperliquid#cancelAllOrdersAfter
     * @description dead man's switch, cancel all orders after the given timeout
     * @param {number} timeout time in milliseconds, 0 represents cancel the timer
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.vaultAddress] the vault address
     * @returns {object} the api result
     */
    async cancelAllOrdersAfter(timeout, params = {}) {
        this.checkRequiredCredentials();
        await this.loadMarkets();
        params = this.omit(params, ['clientOrderId', 'client_id']);
        const nonce = this.milliseconds();
        const request = {
            'nonce': nonce,
            // 'vaultAddress': vaultAddress,
        };
        const cancelAction = {
            'type': 'scheduleCancel',
            'time': nonce + timeout,
        };
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'cancelAllOrdersAfter', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        const signature = this.signL1Action(cancelAction, nonce, vaultAddress);
        request['action'] = cancelAction;
        request['signature'] = signature;
        if (vaultAddress !== undefined) {
            params = this.omit(params, 'vaultAddress');
            request['vaultAddress'] = vaultAddress;
        }
        const response = await this.privatePostExchange(request);
        //
        //     {
        //         "status":"err",
        //         "response":"Cannot set scheduled cancel time until enough volume traded. Required: $1000000. Traded: $373.47205."
        //     }
        //
        return response;
    }
    editOrdersRequest(orders, params = {}) {
        this.checkRequiredCredentials();
        let hasClientOrderId = false;
        for (let i = 0; i < orders.length; i++) {
            const rawOrder = orders[i];
            const orderParams = this.safeDict(rawOrder, 'params', {});
            const clientOrderId = this.safeString2(orderParams, 'clientOrderId', 'client_id');
            if (clientOrderId !== undefined) {
                hasClientOrderId = true;
            }
        }
        if (hasClientOrderId) {
            for (let i = 0; i < orders.length; i++) {
                const rawOrder = orders[i];
                const orderParams = this.safeDict(rawOrder, 'params', {});
                const clientOrderId = this.safeString2(orderParams, 'clientOrderId', 'client_id');
                if (clientOrderId === undefined) {
                    throw new errors.ArgumentsRequired(this.id + ' editOrders() all orders must have clientOrderId if at least one has a clientOrderId');
                }
            }
        }
        params = this.omit(params, ['slippage', 'clientOrderId', 'client_id', 'slippage', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'timeInForce']);
        const modifies = [];
        for (let i = 0; i < orders.length; i++) {
            const rawOrder = orders[i];
            const id = this.safeString(rawOrder, 'id');
            const marketId = this.safeString(rawOrder, 'symbol');
            const market = this.market(marketId);
            const symbol = market['symbol'];
            const type = this.safeStringUpper(rawOrder, 'type');
            const isMarket = (type === 'MARKET');
            const side = this.safeStringUpper(rawOrder, 'side');
            const isBuy = (side === 'BUY');
            const amount = this.safeString(rawOrder, 'amount');
            const price = this.safeString(rawOrder, 'price');
            let orderParams = this.safeDict(rawOrder, 'params', {});
            const defaultSlippage = this.safeString(this.options, 'defaultSlippage');
            const slippage = this.safeString(orderParams, 'slippage', defaultSlippage);
            let defaultTimeInForce = (isMarket) ? 'ioc' : 'gtc';
            const postOnly = this.safeBool(orderParams, 'postOnly', false);
            if (postOnly) {
                defaultTimeInForce = 'alo';
            }
            let timeInForce = this.safeStringLower(orderParams, 'timeInForce', defaultTimeInForce);
            timeInForce = this.capitalize(timeInForce);
            const clientOrderId = this.safeString2(orderParams, 'clientOrderId', 'client_id');
            let triggerPrice = this.safeString2(orderParams, 'triggerPrice', 'stopPrice');
            const stopLossPrice = this.safeString(orderParams, 'stopLossPrice', triggerPrice);
            const takeProfitPrice = this.safeString(orderParams, 'takeProfitPrice');
            const isTrigger = (stopLossPrice || takeProfitPrice);
            const reduceOnly = this.safeBool(orderParams, 'reduceOnly', false);
            orderParams = this.omit(orderParams, ['slippage', 'timeInForce', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'clientOrderId', 'client_id', 'postOnly', 'reduceOnly']);
            let px = this.numberToString(price);
            if (isMarket) {
                px = (isBuy) ? Precise["default"].stringMul(px, Precise["default"].stringAdd('1', slippage)) : Precise["default"].stringMul(px, Precise["default"].stringSub('1', slippage));
                px = this.priceToPrecision(symbol, px);
            }
            else {
                px = this.priceToPrecision(symbol, px);
            }
            const sz = this.amountToPrecision(symbol, amount);
            const orderType = {};
            if (isTrigger) {
                let isTp = false;
                if (takeProfitPrice !== undefined) {
                    triggerPrice = this.priceToPrecision(symbol, takeProfitPrice);
                    isTp = true;
                }
                else {
                    triggerPrice = this.priceToPrecision(symbol, stopLossPrice);
                }
                orderType['trigger'] = {
                    'isMarket': isMarket,
                    'triggerPx': triggerPrice,
                    'tpsl': (isTp) ? 'tp' : 'sl',
                };
            }
            else {
                orderType['limit'] = {
                    'tif': timeInForce,
                };
            }
            if (triggerPrice === undefined) {
                triggerPrice = '0';
            }
            const orderReq = {
                'a': this.parseToInt(market['baseId']),
                'b': isBuy,
                'p': px,
                's': sz,
                'r': reduceOnly,
                't': orderType,
                // 'c': clientOrderId,
            };
            if (clientOrderId !== undefined) {
                orderReq['c'] = clientOrderId;
            }
            const modifyReq = {
                'oid': this.parseToInt(id),
                'order': orderReq,
            };
            modifies.push(modifyReq);
        }
        const nonce = this.milliseconds();
        const modifyAction = {
            'type': 'batchModify',
            'modifies': modifies,
        };
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'editOrder', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        const signature = this.signL1Action(modifyAction, nonce, vaultAddress);
        const request = {
            'action': modifyAction,
            'nonce': nonce,
            'signature': signature,
            // 'vaultAddress': vaultAddress,
        };
        if (vaultAddress !== undefined) {
            request['vaultAddress'] = vaultAddress;
        }
        return request;
    }
    /**
     * @method
     * @name hyperliquid#editOrder
     * @description edit a trade order
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
     * @param {string} id cancel order id
     * @param {string} symbol unified symbol of the market to create an order in
     * @param {string} type 'market' or 'limit'
     * @param {string} side 'buy' or 'sell'
     * @param {float} amount how much of currency you want to trade in units of base currency
     * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [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 {string} [params.clientOrderId] client order id, (optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef)
     * @param {string} [params.vaultAddress] the vault address for order
     * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
        await this.loadMarkets();
        if (id === undefined) {
            throw new errors.ArgumentsRequired(this.id + ' editOrder() requires an id argument');
        }
        const [order, globalParams] = this.parseCreateEditOrderArgs(id, symbol, type, side, amount, price, params);
        const orders = await this.editOrders([order], globalParams);
        return orders[0];
    }
    /**
     * @method
     * @name hyperliquid#editOrders
     * @description edit a list of trade orders
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
     * @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
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async editOrders(orders, params = {}) {
        await this.loadMarkets();
        const request = this.editOrdersRequest(orders, params);
        const response = await this.privatePostExchange(request);
        //
        //     {
        //         "status": "ok",
        //         "response": {
        //             "type": "order",
        //             "data": {
        //                 "statuses": [
        //                     {
        //                         "resting": {
        //                             "oid": 5063830287
        //                         }
        //                     }
        //                 ]
        //             }
        //         }
        //     }
        // when the order is filled immediately
        //     {
        //         "status":"ok",
        //         "response":{
        //            "type":"order",
        //            "data":{
        //               "statuses":[
        //                  {
        //                     "filled":{
        //                        "totalSz":"0.1",
        //                        "avgPx":"100.84",
        //                        "oid":6195281425
        //                     }
        //                  }
        //               ]
        //            }
        //         }
        //     }
        //
        const responseObject = this.safeDict(response, 'response', {});
        const dataObject = this.safeDict(responseObject, 'data', {});
        const statuses = this.safeList(dataObject, 'statuses', []);
        return this.parseOrders(statuses);
    }
    /**
     * @method
     * @name hyperliquid#createVault
     * @description creates a value
     * @param {string} name The name of the vault
     * @param {string} description The description of the vault
     * @param {number} initialUsd The initialUsd of the vault
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} the api result
     */
    async createVault(name, description, initialUsd, params = {}) {
        this.checkRequiredCredentials();
        await this.loadMarkets();
        const nonce = this.milliseconds();
        const request = {
            'nonce': nonce,
        };
        const usd = this.parseToInt(Precise["default"].stringMul(this.numberToString(initialUsd), '1000000'));
        const action = {
            'type': 'createVault',
            'name': name,
            'description': description,
            'initialUsd': usd,
            'nonce': nonce,
        };
        const signature = this.signL1Action(action, nonce);
        request['action'] = action;
        request['signature'] = signature;
        const response = await this.privatePostExchange(this.extend(request, params));
        //
        // {
        //     "status": "ok",
        //     "response": {
        //         "type": "createVault",
        //         "data": "0x04fddcbc9ce80219301bd16f18491bedf2a8c2b8"
        //     }
        // }
        //
        return response;
    }
    /**
     * @method
     * @name hyperliquid#fetchFundingRateHistory
     * @description fetches historical funding rate prices
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-historical-funding-rates
     * @param {string} symbol unified symbol of the market to fetch the funding rate history for
     * @param {int} [since] timestamp in ms of the earliest funding rate to fetch
     * @param {int} [limit] the maximum amount of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure} to fetch
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {int} [params.until] timestamp in ms of the latest funding rate
     * @returns {object[]} a list of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure}
     */
    async fetchFundingRateHistory(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        if (symbol === undefined) {
            throw new errors.ArgumentsRequired(this.id + ' fetchFundingRateHistory() requires a symbol argument');
        }
        const market = this.market(symbol);
        const request = {
            'type': 'fundingHistory',
            'coin': market['base'],
        };
        if (since !== undefined) {
            request['startTime'] = since;
        }
        else {
            const maxLimit = (limit === undefined) ? 500 : limit;
            request['startTime'] = this.milliseconds() - maxLimit * 60 * 60 * 1000;
        }
        const until = this.safeInteger(params, 'until');
        params = this.omit(params, 'until');
        if (until !== undefined) {
            request['endTime'] = until;
        }
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        //     [
        //         {
        //             "coin": "ETH",
        //             "fundingRate": "0.0000125",
        //             "premium": "0.00057962",
        //             "time": 1704290400031
        //         }
        //     ]
        //
        const result = [];
        for (let i = 0; i < response.length; i++) {
            const entry = response[i];
            const timestamp = this.safeInteger(entry, 'time');
            result.push({
                'info': entry,
                'symbol': this.safeSymbol(undefined, market),
                'fundingRate': this.safeNumber(entry, 'fundingRate'),
                'timestamp': timestamp,
                'datetime': this.iso8601(timestamp),
            });
        }
        const sorted = this.sortBy(result, 'timestamp');
        return this.filterBySymbolSinceLimit(sorted, symbol, since, limit);
    }
    /**
     * @method
     * @name hyperliquid#fetchOpenOrders
     * @description fetch all unfilled currently open orders
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#retrieve-a-users-open-orders
     * @param {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch open orders for
     * @param {int} [limit] the maximum number of open orders structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @param {string} [params.method] 'openOrders' or 'frontendOpenOrders' default is 'frontendOpenOrders'
     * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchOpenOrders', params);
        let method = undefined;
        [method, params] = this.handleOptionAndParams(params, 'fetchOpenOrders', 'method', 'frontendOpenOrders');
        await this.loadMarkets();
        const market = this.safeMarket(symbol);
        const request = {
            'type': method,
            'user': userAddress,
        };
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        //     [
        //         {
        //             "coin": "ETH",
        //             "limitPx": "2000.0",
        //             "oid": 3991946565,
        //             "origSz": "0.1",
        //             "side": "B",
        //             "sz": "0.1",
        //             "timestamp": 1704346468838
        //         }
        //     ]
        //
        const orderWithStatus = [];
        for (let i = 0; i < response.length; i++) {
            const order = response[i];
            const extendOrder = {};
            if (this.safeString(order, 'status') === undefined) {
                extendOrder['ccxtStatus'] = 'open';
            }
            orderWithStatus.push(this.extend(order, extendOrder));
        }
        return this.parseOrders(orderWithStatus, market, since, limit);
    }
    /**
     * @method
     * @name hyperliquid#fetchClosedOrders
     * @description fetch all unfilled currently closed orders
     * @param {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch open orders for
     * @param {int} [limit] the maximum number of open orders structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        const orders = await this.fetchOrders(symbol, undefined, undefined, params); // don't filter here because we don't want to catch open orders
        const closedOrders = this.filterByArray(orders, 'status', ['closed'], false);
        return this.filterBySymbolSinceLimit(closedOrders, symbol, since, limit);
    }
    /**
     * @method
     * @name hyperliquid#fetchCanceledOrders
     * @description fetch all canceled orders
     * @param {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch open orders for
     * @param {int} [limit] the maximum number of open orders structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchCanceledOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        const orders = await this.fetchOrders(symbol, undefined, undefined, params); // don't filter here because we don't want to catch open orders
        const closedOrders = this.filterByArray(orders, 'status', ['canceled'], false);
        return this.filterBySymbolSinceLimit(closedOrders, symbol, since, limit);
    }
    /**
     * @method
     * @name hyperliquid#fetchCanceledAndClosedOrders
     * @description fetch all closed and canceled orders
     * @param {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch open orders for
     * @param {int} [limit] the maximum number of open orders structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchCanceledAndClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        const orders = await this.fetchOrders(symbol, undefined, undefined, params); // don't filter here because we don't want to catch open orders
        const closedOrders = this.filterByArray(orders, 'status', ['canceled', 'closed', 'rejected'], false);
        return this.filterBySymbolSinceLimit(closedOrders, symbol, since, limit);
    }
    /**
     * @method
     * @name hyperliquid#fetchOrders
     * @description fetch all orders
     * @param {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch open orders for
     * @param {int} [limit] the maximum number of open orders structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchOrders', params);
        await this.loadMarkets();
        const market = this.safeMarket(symbol);
        const request = {
            'type': 'historicalOrders',
            'user': userAddress,
        };
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        //     [
        //         {
        //             "coin": "ETH",
        //             "limitPx": "2000.0",
        //             "oid": 3991946565,
        //             "origSz": "0.1",
        //             "side": "B",
        //             "sz": "0.1",
        //             "timestamp": 1704346468838
        //         }
        //     ]
        //
        return this.parseOrders(response, market, since, limit);
    }
    /**
     * @method
     * @name hyperliquid#fetchOrder
     * @description 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 {string} id order id
     * @param {string} symbol unified symbol of the market the order was made in
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
     */
    async fetchOrder(id, symbol = undefined, params = {}) {
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchOrder', params);
        await this.loadMarkets();
        const market = this.safeMarket(symbol);
        const isClientOrderId = id.length >= 34;
        const request = {
            'type': 'orderStatus',
            'oid': isClientOrderId ? id : this.parseToNumeric(id),
            'user': userAddress,
        };
        const response = await this.publicPostInfo(this.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"
        //     }
        //
        const data = this.safeDict(response, 'order');
        return this.parseOrder(data, market);
    }
    parseOrder(order, market = undefined) {
        //
        // createOrdersWs error
        //
        //  {error: 'Insufficient margin to place order. asset=159'}
        //
        //  fetchOpenOrders
        //
        //     {
        //         "coin": "ETH",
        //         "limitPx": "2000.0",
        //         "oid": 3991946565,
        //         "origSz": "0.1",
        //         "side": "B",
        //         "sz": "0.1",
        //         "timestamp": 1704346468838
        //     }
        // fetchClosedorders
        //    {
        //        "cloid": null,
        //        "closedPnl": "0.0",
        //        "coin": "SOL",
        //        "crossed": true,
        //        "dir": "Open Long",
        //        "fee": "0.003879",
        //        "hash": "0x4a2647998682b7f07bc5040ab531e1011400f9a51bfa0346a0b41ebe510e8875",
        //        "liquidationMarkPx": null,
        //        "oid": "6463280784",
        //        "px": "110.83",
        //        "side": "B",
        //        "startPosition": "1.64",
        //        "sz": "0.1",
        //        "tid": "232174667018988",
        //        "time": "1709142268394"
        //    }
        //
        //  fetchOrder
        //
        //     {
        //         "order": {
        //             "children": [],
        //             "cloid": null,
        //             "coin": "ETH",
        //             "isPositionTpsl": false,
        //             "isTrigger": false,
        //             "limitPx": "2000.0",
        //             "oid": "3991946565",
        //             "orderType": "Limit",
        //             "origSz": "0.1",
        //             "reduceOnly": false,
        //             "side": "B",
        //             "sz": "0.1",
        //             "tif": "Gtc",
        //             "timestamp": "1704346468838",
        //             "triggerCondition": "N/A",
        //             "triggerPx": "0.0"
        //         },
        //         "status": "open",
        //         "statusTimestamp": "1704346468838"
        //     }
        //
        // createOrder
        //
        //     {
        //         "resting": {
        //             "oid": 5063830287
        //         }
        //     }
        //
        //     {
        //        "filled":{
        //           "totalSz":"0.1",
        //           "avgPx":"100.84",
        //           "oid":6195281425
        //        }
        //     }
        // frontendOrder
        // {
        //     "children": [],
        //     "cloid": null,
        //     "coin": "BLUR",
        //     "isPositionTpsl": false,
        //     "isTrigger": true,
        //     "limitPx": "0.5",
        //     "oid": 8670487141,
        //     "orderType": "Stop Limit",
        //     "origSz": "20.0",
        //     "reduceOnly": false,
        //     "side": "B",
        //     "sz": "20.0",
        //     "tif": null,
        //     "timestamp": 1715523663687,
        //     "triggerCondition": "Price above 0.6",
        //     "triggerPx": "0.6"
        // }
        //
        const error = this.safeString(order, 'error');
        if (error !== undefined) {
            return this.safeOrder({
                'info': order,
                'status': 'rejected',
            });
        }
        let entry = this.safeDictN(order, ['order', 'resting', 'filled']);
        if (entry === undefined) {
            entry = order;
        }
        const coin = this.safeString(entry, 'coin');
        let marketId = undefined;
        if (coin !== undefined) {
            marketId = this.coinToMarketId(coin);
        }
        if (this.safeString(entry, 'id') === undefined) {
            market = this.safeMarket(marketId, undefined);
        }
        else {
            market = this.safeMarket(marketId, market);
        }
        const symbol = market['symbol'];
        const timestamp = this.safeInteger(entry, 'timestamp');
        const status = this.safeString2(order, 'status', 'ccxtStatus');
        order = this.omit(order, ['ccxtStatus']);
        let side = this.safeString(entry, 'side');
        if (side !== undefined) {
            side = (side === 'A') ? 'sell' : 'buy';
        }
        const totalAmount = this.safeString2(entry, 'origSz', 'totalSz');
        const remaining = this.safeString(entry, 'sz');
        const tif = this.safeStringUpper(entry, 'tif');
        let postOnly = undefined;
        if (tif !== undefined) {
            postOnly = (tif === 'ALO');
        }
        return this.safeOrder({
            'info': order,
            'id': this.safeString(entry, 'oid'),
            'clientOrderId': this.safeString(entry, 'cloid'),
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'lastTradeTimestamp': undefined,
            'lastUpdateTimestamp': this.safeInteger(order, 'statusTimestamp'),
            'symbol': symbol,
            'type': this.parseOrderType(this.safeStringLower(entry, 'orderType')),
            'timeInForce': tif,
            'postOnly': postOnly,
            'reduceOnly': this.safeBool(entry, 'reduceOnly'),
            'side': side,
            'price': this.safeString(entry, 'limitPx'),
            'triggerPrice': this.safeBool(entry, 'isTrigger') ? this.safeNumber(entry, 'triggerPx') : undefined,
            'amount': totalAmount,
            'cost': undefined,
            'average': this.safeString(entry, 'avgPx'),
            'filled': Precise["default"].stringSub(totalAmount, remaining),
            'remaining': remaining,
            'status': this.parseOrderStatus(status),
            'fee': undefined,
            'trades': undefined,
        }, market);
    }
    parseOrderStatus(status) {
        const statuses = {
            'triggered': 'open',
            'filled': 'closed',
            'open': 'open',
            'canceled': 'canceled',
            'rejected': 'rejected',
            'marginCanceled': 'canceled',
        };
        return this.safeString(statuses, status, status);
    }
    parseOrderType(status) {
        const statuses = {
            'stop limit': 'limit',
            'stop market': 'market',
        };
        return this.safeString(statuses, status, status);
    }
    /**
     * @method
     * @name hyperliquid#fetchMyTrades
     * @description 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 {string} symbol unified market symbol
     * @param {int} [since] the earliest time in ms to fetch trades for
     * @param {int} [limit] the maximum number of trades structures to retrieve
     * @param {object} [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]{@link https://docs.ccxt.com/#/?id=trade-structure}
     */
    async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchMyTrades', params);
        await this.loadMarkets();
        const market = this.safeMarket(symbol);
        const request = {
            'user': userAddress,
        };
        if (since !== undefined) {
            request['type'] = 'userFillsByTime';
            request['startTime'] = since;
        }
        else {
            request['type'] = 'userFills';
        }
        const until = this.safeInteger(params, 'until');
        params = this.omit(params, 'until');
        if (until !== undefined) {
            request['endTime'] = until;
        }
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        //     [
        //         {
        //             "closedPnl": "0.19343",
        //             "coin": "ETH",
        //             "crossed": true,
        //             "dir": "Close Long",
        //             "fee": "0.050062",
        //             "feeToken": "USDC",
        //             "hash": "0x09d77c96791e98b5775a04092584ab010d009445119c71e4005c0d634ea322bc",
        //             "liquidationMarkPx": null,
        //             "oid": 3929354691,
        //             "px": "2381.1",
        //             "side": "A",
        //             "startPosition": "0.0841",
        //             "sz": "0.0841",
        //             "tid": 128423918764978,
        //             "time": 1704262888911
        //         }
        //     ]
        //
        return this.parseTrades(response, market, since, limit);
    }
    parseTrade(trade, market = undefined) {
        //
        //     {
        //         "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
        //     }
        //
        const timestamp = this.safeInteger(trade, 'time');
        const price = this.safeString(trade, 'px');
        const amount = this.safeString(trade, 'sz');
        const coin = this.safeString(trade, 'coin');
        const marketId = this.coinToMarketId(coin);
        market = this.safeMarket(marketId, undefined);
        const symbol = market['symbol'];
        const id = this.safeString(trade, 'tid');
        let side = this.safeString(trade, 'side');
        if (side !== undefined) {
            side = (side === 'A') ? 'sell' : 'buy';
        }
        const fee = this.safeString(trade, 'fee');
        let takerOrMaker = undefined;
        const crossed = this.safeBool(trade, 'crossed');
        if (crossed !== undefined) {
            takerOrMaker = crossed ? 'taker' : 'maker';
        }
        return this.safeTrade({
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'symbol': symbol,
            'id': id,
            'order': this.safeString(trade, 'oid'),
            'type': undefined,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': price,
            'amount': amount,
            'cost': undefined,
            'fee': {
                'cost': fee,
                'currency': this.safeString(trade, 'feeToken'),
                'rate': undefined,
            },
        }, market);
    }
    /**
     * @method
     * @name hyperliquid#fetchPosition
     * @description fetch data on an open position
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
     * @param {string} symbol unified market symbol of the market the position is held in
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @returns {object} a [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
     */
    async fetchPosition(symbol, params = {}) {
        const positions = await this.fetchPositions([symbol], params);
        return this.safeDict(positions, 0, {});
    }
    /**
     * @method
     * @name hyperliquid#fetchPositions
     * @description fetch all open positions
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
     * @param {string[]} [symbols] list of unified market symbols
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
     */
    async fetchPositions(symbols = undefined, params = {}) {
        await this.loadMarkets();
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchPositions', params);
        symbols = this.marketSymbols(symbols);
        const request = {
            'type': 'clearinghouseState',
            'user': userAddress,
        };
        const response = await this.publicPostInfo(this.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"
        //     }
        //
        const data = this.safeList(response, 'assetPositions', []);
        const result = [];
        for (let i = 0; i < data.length; i++) {
            result.push(this.parsePosition(data[i], undefined));
        }
        return this.filterByArrayPositions(result, 'symbol', symbols, false);
    }
    parsePosition(position, market = undefined) {
        //
        //     {
        //         "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"
        //     }
        //
        const entry = this.safeDict(position, 'position', {});
        const coin = this.safeString(entry, 'coin');
        const marketId = this.coinToMarketId(coin);
        market = this.safeMarket(marketId, undefined);
        const symbol = market['symbol'];
        const leverage = this.safeDict(entry, 'leverage', {});
        const marginMode = this.safeString(leverage, 'type');
        const isIsolated = (marginMode === 'isolated');
        const rawSize = this.safeString(entry, 'szi');
        let size = rawSize;
        let side = undefined;
        if (size !== undefined) {
            side = Precise["default"].stringGt(rawSize, '0') ? 'long' : 'short';
            size = Precise["default"].stringAbs(size);
        }
        const rawUnrealizedPnl = this.safeString(entry, 'unrealizedPnl');
        const absRawUnrealizedPnl = Precise["default"].stringAbs(rawUnrealizedPnl);
        const initialMargin = this.safeString(entry, 'marginUsed');
        const percentage = Precise["default"].stringMul(Precise["default"].stringDiv(absRawUnrealizedPnl, initialMargin), '100');
        return this.safePosition({
            'info': position,
            'id': undefined,
            'symbol': symbol,
            'timestamp': undefined,
            'datetime': undefined,
            'isolated': isIsolated,
            'hedged': undefined,
            'side': side,
            'contracts': this.parseNumber(size),
            'contractSize': undefined,
            'entryPrice': this.safeNumber(entry, 'entryPx'),
            'markPrice': undefined,
            'notional': this.safeNumber(entry, 'positionValue'),
            'leverage': this.safeNumber(leverage, 'value'),
            'collateral': this.safeNumber(entry, 'marginUsed'),
            'initialMargin': this.parseNumber(initialMargin),
            'maintenanceMargin': undefined,
            'initialMarginPercentage': undefined,
            'maintenanceMarginPercentage': undefined,
            'unrealizedPnl': this.parseNumber(rawUnrealizedPnl),
            'liquidationPrice': this.safeNumber(entry, 'liquidationPx'),
            'marginMode': marginMode,
            'percentage': this.parseNumber(percentage),
        });
    }
    /**
     * @method
     * @name hyperliquid#setMarginMode
     * @description set margin mode (symbol)
     * @param {string} marginMode margin mode must be either [isolated, cross]
     * @param {string} symbol unified market symbol of the market the position is held in, default is undefined
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.leverage] the rate of leverage, is required if setting trade mode (symbol)
     * @returns {object} response from the exchange
     */
    async setMarginMode(marginMode, symbol = undefined, params = {}) {
        if (symbol === undefined) {
            throw new errors.ArgumentsRequired(this.id + ' setMarginMode() requires a symbol argument');
        }
        await this.loadMarkets();
        const market = this.market(symbol);
        const leverage = this.safeInteger(params, 'leverage');
        if (leverage === undefined) {
            throw new errors.ArgumentsRequired(this.id + ' setMarginMode() requires a leverage parameter');
        }
        const asset = this.parseToInt(market['baseId']);
        const isCross = (marginMode === 'cross');
        const nonce = this.milliseconds();
        params = this.omit(params, ['leverage']);
        const updateAction = {
            'type': 'updateLeverage',
            'asset': asset,
            'isCross': isCross,
            'leverage': leverage,
        };
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'setMarginMode', 'vaultAddress');
        if (vaultAddress !== undefined) {
            if (vaultAddress.startsWith('0x')) {
                vaultAddress = vaultAddress.replace('0x', '');
            }
        }
        const signature = this.signL1Action(updateAction, nonce, vaultAddress);
        const request = {
            'action': updateAction,
            'nonce': nonce,
            'signature': signature,
            // 'vaultAddress': vaultAddress,
        };
        if (vaultAddress !== undefined) {
            request['vaultAddress'] = vaultAddress;
        }
        const response = await this.privatePostExchange(request);
        //
        //     {
        //         'response': {
        //             'type': 'default'
        //         },
        //         'status': 'ok'
        //     }
        //
        return response;
    }
    /**
     * @method
     * @name hyperliquid#setLeverage
     * @description set the level of leverage for a market
     * @param {float} leverage the rate of leverage
     * @param {string} symbol unified market symbol
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.marginMode] margin mode must be either [isolated, cross], default is cross
     * @returns {object} response from the exchange
     */
    async setLeverage(leverage, symbol = undefined, params = {}) {
        if (symbol === undefined) {
            throw new errors.ArgumentsRequired(this.id + ' setLeverage() requires a symbol argument');
        }
        await this.loadMarkets();
        const market = this.market(symbol);
        const marginMode = this.safeString(params, 'marginMode', 'cross');
        const isCross = (marginMode === 'cross');
        const asset = this.parseToInt(market['baseId']);
        const nonce = this.milliseconds();
        params = this.omit(params, 'marginMode');
        const updateAction = {
            'type': 'updateLeverage',
            'asset': asset,
            'isCross': isCross,
            'leverage': leverage,
        };
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'setLeverage', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        const signature = this.signL1Action(updateAction, nonce, vaultAddress);
        const request = {
            'action': updateAction,
            'nonce': nonce,
            'signature': signature,
            // 'vaultAddress': vaultAddress,
        };
        if (vaultAddress !== undefined) {
            params = this.omit(params, 'vaultAddress');
            request['vaultAddress'] = vaultAddress;
        }
        const response = await this.privatePostExchange(request);
        //
        //     {
        //         'response': {
        //             'type': 'default'
        //         },
        //         'status': 'ok'
        //     }
        //
        return response;
    }
    /**
     * @method
     * @name hyperliquid#addMargin
     * @description add margin
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
     * @param {string} symbol unified market symbol
     * @param {float} amount amount of margin to add
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} a [margin structure]{@link https://docs.ccxt.com/#/?id=add-margin-structure}
     */
    async addMargin(symbol, amount, params = {}) {
        return await this.modifyMarginHelper(symbol, amount, 'add', params);
    }
    /**
     * @method
     * @name hyperliquid#reduceMargin
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
     * @description remove margin from a position
     * @param {string} symbol unified market symbol
     * @param {float} amount the amount of margin to remove
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} a [margin structure]{@link https://docs.ccxt.com/#/?id=reduce-margin-structure}
     */
    async reduceMargin(symbol, amount, params = {}) {
        return await this.modifyMarginHelper(symbol, amount, 'reduce', params);
    }
    async modifyMarginHelper(symbol, amount, type, params = {}) {
        await this.loadMarkets();
        const market = this.market(symbol);
        const asset = this.parseToInt(market['baseId']);
        let sz = this.parseToInt(Precise["default"].stringMul(this.amountToPrecision(symbol, amount), '1000000'));
        if (type === 'reduce') {
            sz = -sz;
        }
        const nonce = this.milliseconds();
        const updateAction = {
            'type': 'updateIsolatedMargin',
            'asset': asset,
            'isBuy': true,
            'ntli': sz,
        };
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'modifyMargin', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        const signature = this.signL1Action(updateAction, nonce, vaultAddress);
        const request = {
            'action': updateAction,
            'nonce': nonce,
            'signature': signature,
            // 'vaultAddress': vaultAddress,
        };
        if (vaultAddress !== undefined) {
            request['vaultAddress'] = vaultAddress;
        }
        const response = await this.privatePostExchange(request);
        //
        //     {
        //         'response': {
        //             'type': 'default'
        //         },
        //         'status': 'ok'
        //     }
        //
        return this.extend(this.parseMarginModification(response, market), {
            'code': this.safeString(response, 'status'),
        });
    }
    parseMarginModification(data, market = undefined) {
        //
        //    {
        //        'type': 'default'
        //    }
        //
        return {
            'info': data,
            'symbol': this.safeSymbol(undefined, market),
            'type': undefined,
            'marginMode': 'isolated',
            'amount': undefined,
            'total': undefined,
            'code': this.safeString(market, 'settle'),
            'status': undefined,
            'timestamp': undefined,
            'datetime': undefined,
        };
    }
    /**
     * @method
     * @name hyperliquid#transfer
     * @description 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 {string} code unified currency code
     * @param {float} amount amount to transfer
     * @param {string} fromAccount account to transfer from *spot, swap*
     * @param {string} toAccount account to transfer to *swap, spot or address*
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.vaultAddress] the vault address for order
     * @returns {object} a [transfer structure]{@link https://docs.ccxt.com/#/?id=transfer-structure}
     */
    async transfer(code, amount, fromAccount, toAccount, params = {}) {
        this.checkRequiredCredentials();
        await this.loadMarkets();
        const isSandboxMode = this.safeBool(this.options, 'sandboxMode');
        const nonce = this.milliseconds();
        if (this.inArray(fromAccount, ['spot', 'swap', 'perp'])) {
            // handle swap <> spot account transfer
            if (!this.inArray(toAccount, ['spot', 'swap', 'perp'])) {
                throw new errors.NotSupported(this.id + ' transfer() only support spot <> swap transfer');
            }
            let strAmount = this.numberToString(amount);
            let vaultAddress = undefined;
            [vaultAddress, params] = this.handleOptionAndParams(params, 'transfer', 'vaultAddress');
            vaultAddress = this.formatVaultAddress(vaultAddress);
            if (vaultAddress !== undefined) {
                strAmount = strAmount + ' subaccount:' + vaultAddress;
            }
            const toPerp = (toAccount === 'perp') || (toAccount === 'swap');
            const transferPayload = {
                'hyperliquidChain': isSandboxMode ? 'Testnet' : 'Mainnet',
                'amount': strAmount,
                'toPerp': toPerp,
                'nonce': nonce,
            };
            const transferSig = this.buildUsdClassSendSig(transferPayload);
            const transferRequest = {
                'action': {
                    'hyperliquidChain': transferPayload['hyperliquidChain'],
                    'signatureChainId': '0x66eee',
                    'type': 'usdClassTransfer',
                    'amount': strAmount,
                    'toPerp': toPerp,
                    'nonce': nonce,
                },
                'nonce': nonce,
                'signature': transferSig,
            };
            if (vaultAddress !== undefined) {
                transferRequest['vaultAddress'] = vaultAddress;
            }
            const transferResponse = await this.privatePostExchange(transferRequest);
            return transferResponse;
        }
        // handle sub-account/different account transfer
        this.checkAddress(toAccount);
        if (code !== undefined) {
            code = code.toUpperCase();
            if (code !== 'USDC') {
                throw new errors.NotSupported(this.id + ' transfer() only support USDC');
            }
        }
        const payload = {
            'hyperliquidChain': isSandboxMode ? 'Testnet' : 'Mainnet',
            'destination': toAccount,
            'amount': this.numberToString(amount),
            'time': nonce,
        };
        const sig = this.buildUsdSendSig(payload);
        const request = {
            'action': {
                'hyperliquidChain': payload['hyperliquidChain'],
                'signatureChainId': '0x66eee',
                'destination': toAccount,
                'amount': amount.toString(),
                'time': nonce,
                'type': 'usdSend',
            },
            'nonce': nonce,
            'signature': sig,
        };
        const response = await this.privatePostExchange(request);
        //
        // {'response': {'type': 'default'}, 'status': 'ok'}
        //
        return this.parseTransfer(response);
    }
    parseTransfer(transfer, currency = undefined) {
        //
        // {'response': {'type': 'default'}, 'status': 'ok'}
        //
        return {
            'info': transfer,
            'id': undefined,
            'timestamp': undefined,
            'datetime': undefined,
            'currency': undefined,
            'amount': undefined,
            'fromAccount': undefined,
            'toAccount': undefined,
            'status': 'ok',
        };
    }
    /**
     * @method
     * @name hyperliquid#withdraw
     * @description make a withdrawal (only support USDC)
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#initiate-a-withdrawal-request
     * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#deposit-or-withdraw-from-a-vault
     * @param {string} code unified currency code
     * @param {float} amount the amount to withdraw
     * @param {string} address the address to withdraw to
     * @param {string} tag
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.vaultAddress] vault address withdraw from
     * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
     */
    async withdraw(code, amount, address, tag = undefined, params = {}) {
        this.checkRequiredCredentials();
        await this.loadMarkets();
        this.checkAddress(address);
        if (code !== undefined) {
            code = code.toUpperCase();
            if (code !== 'USDC') {
                throw new errors.NotSupported(this.id + ' withdraw() only support USDC');
            }
        }
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'withdraw', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        params = this.omit(params, 'vaultAddress');
        const nonce = this.milliseconds();
        let action = {};
        let sig = undefined;
        if (vaultAddress !== undefined) {
            action = {
                'type': 'vaultTransfer',
                'vaultAddress': '0x' + vaultAddress,
                'isDeposit': false,
                'usd': amount,
            };
            sig = this.signL1Action(action, nonce);
        }
        else {
            const isSandboxMode = this.safeBool(this.options, 'sandboxMode', false);
            const payload = {
                'hyperliquidChain': isSandboxMode ? 'Testnet' : 'Mainnet',
                'destination': address,
                'amount': amount.toString(),
                'time': nonce,
            };
            sig = this.buildWithdrawSig(payload);
            action = {
                'hyperliquidChain': payload['hyperliquidChain'],
                'signatureChainId': '0x66eee',
                'destination': address,
                'amount': amount.toString(),
                'time': nonce,
                'type': 'withdraw3',
            };
        }
        const request = {
            'action': action,
            'nonce': nonce,
            'signature': sig,
        };
        const response = await this.privatePostExchange(request);
        return this.parseTransaction(response);
    }
    parseTransaction(transaction, currency = undefined) {
        //
        // { status: 'ok', response: { type: 'default' } }
        //
        // fetchDeposits / fetchWithdrawals
        // {
        //     "time":1724762307531,
        //     "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
        //     "delta":{
        //         "type":"accountClassTransfer",
        //         "usdc":"50.0",
        //         "toPerp":false
        //     }
        // }
        //
        const timestamp = this.safeInteger(transaction, 'time');
        const delta = this.safeDict(transaction, 'delta', {});
        let fee = undefined;
        const feeCost = this.safeInteger(delta, 'fee');
        if (feeCost !== undefined) {
            fee = {
                'currency': 'USDC',
                'cost': feeCost,
            };
        }
        let internal = undefined;
        const type = this.safeString(delta, 'type');
        if (type !== undefined) {
            internal = (type === 'internalTransfer');
        }
        return {
            'info': transaction,
            'id': undefined,
            'txid': this.safeString(transaction, 'hash'),
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'network': undefined,
            'address': undefined,
            'addressTo': this.safeString(delta, 'destination'),
            'addressFrom': this.safeString(delta, 'user'),
            'tag': undefined,
            'tagTo': undefined,
            'tagFrom': undefined,
            'type': undefined,
            'amount': this.safeNumber(delta, 'usdc'),
            'currency': undefined,
            'status': this.safeString(transaction, 'status'),
            'updated': undefined,
            'comment': undefined,
            'internal': internal,
            'fee': fee,
        };
    }
    /**
     * @method
     * @name hyperliquid#fetchTradingFee
     * @description fetch the trading fees for a market
     * @param {string} symbol unified market symbol
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {string} [params.user] user address, will default to this.walletAddress if not provided
     * @returns {object} a [fee structure]{@link https://docs.ccxt.com/#/?id=fee-structure}
     */
    async fetchTradingFee(symbol, params = {}) {
        await this.loadMarkets();
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchTradingFee', params);
        const market = this.market(symbol);
        const request = {
            'type': 'userFees',
            'user': userAddress,
        };
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        //     {
        //         "dailyUserVlm": [
        //             {
        //                 "date": "2024-07-08",
        //                 "userCross": "0.0",
        //                 "userAdd": "0.0",
        //                 "exchange": "90597185.23639999"
        //             }
        //         ],
        //         "feeSchedule": {
        //             "cross": "0.00035",
        //             "add": "0.0001",
        //             "tiers": {
        //                 "vip": [
        //                     {
        //                         "ntlCutoff": "5000000.0",
        //                         "cross": "0.0003",
        //                         "add": "0.00005"
        //                     }
        //                 ],
        //                 "mm": [
        //                     {
        //                         "makerFractionCutoff": "0.005",
        //                         "add": "-0.00001"
        //                     }
        //                 ]
        //             },
        //             "referralDiscount": "0.04"
        //         },
        //         "userCrossRate": "0.00035",
        //         "userAddRate": "0.0001",
        //         "activeReferralDiscount": "0.0"
        //     }
        //
        const data = {
            'userCrossRate': this.safeString(response, 'userCrossRate'),
            'userAddRate': this.safeString(response, 'userAddRate'),
        };
        return this.parseTradingFee(data, market);
    }
    parseTradingFee(fee, market = undefined) {
        //
        //     {
        //         "dailyUserVlm": [
        //             {
        //                 "date": "2024-07-08",
        //                 "userCross": "0.0",
        //                 "userAdd": "0.0",
        //                 "exchange": "90597185.23639999"
        //             }
        //         ],
        //         "feeSchedule": {
        //             "cross": "0.00035",
        //             "add": "0.0001",
        //             "tiers": {
        //                 "vip": [
        //                     {
        //                         "ntlCutoff": "5000000.0",
        //                         "cross": "0.0003",
        //                         "add": "0.00005"
        //                     }
        //                 ],
        //                 "mm": [
        //                     {
        //                         "makerFractionCutoff": "0.005",
        //                         "add": "-0.00001"
        //                     }
        //                 ]
        //             },
        //             "referralDiscount": "0.04"
        //         },
        //         "userCrossRate": "0.00035",
        //         "userAddRate": "0.0001",
        //         "activeReferralDiscount": "0.0"
        //     }
        //
        const symbol = this.safeSymbol(undefined, market);
        return {
            'info': fee,
            'symbol': symbol,
            'maker': this.safeNumber(fee, 'userAddRate'),
            'taker': this.safeNumber(fee, 'userCrossRate'),
            'percentage': undefined,
            'tierBased': undefined,
        };
    }
    /**
     * @method
     * @name hyperliquid#fetchLedger
     * @description fetch the history of changes, actions done by the user or operations that altered the balance of the user
     * @param {string} [code] unified currency code
     * @param {int} [since] timestamp in ms of the earliest ledger entry
     * @param {int} [limit] max number of ledger entries to return
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {int} [params.until] timestamp in ms of the latest ledger entry
     * @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger}
     */
    async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchLedger', params);
        const request = {
            'type': 'userNonFundingLedgerUpdates',
            'user': userAddress,
        };
        if (since !== undefined) {
            request['startTime'] = since;
        }
        const until = this.safeInteger(params, 'until');
        if (until !== undefined) {
            request['endTime'] = until;
            params = this.omit(params, ['until']);
        }
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        // [
        //     {
        //         "time":1724762307531,
        //         "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
        //         "delta":{
        //             "type":"accountClassTransfer",
        //             "usdc":"50.0",
        //             "toPerp":false
        //         }
        //     }
        // ]
        //
        return this.parseLedger(response, undefined, since, limit);
    }
    parseLedgerEntry(item, currency = undefined) {
        //
        // {
        //     "time":1724762307531,
        //     "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
        //     "delta":{
        //         "type":"accountClassTransfer",
        //         "usdc":"50.0",
        //         "toPerp":false
        //     }
        // }
        //
        const timestamp = this.safeInteger(item, 'time');
        const delta = this.safeDict(item, 'delta', {});
        let fee = undefined;
        const feeCost = this.safeInteger(delta, 'fee');
        if (feeCost !== undefined) {
            fee = {
                'currency': 'USDC',
                'cost': feeCost,
            };
        }
        const type = this.safeString(delta, 'type');
        const amount = this.safeString(delta, 'usdc');
        return this.safeLedgerEntry({
            'info': item,
            'id': this.safeString(item, 'hash'),
            'direction': undefined,
            'account': undefined,
            'referenceAccount': this.safeString(delta, 'user'),
            'referenceId': this.safeString(item, 'hash'),
            'type': this.parseLedgerEntryType(type),
            'currency': undefined,
            'amount': this.parseNumber(amount),
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'before': undefined,
            'after': undefined,
            'status': undefined,
            'fee': fee,
        }, currency);
    }
    parseLedgerEntryType(type) {
        const ledgerType = {
            'internalTransfer': 'transfer',
            'accountClassTransfer': 'transfer',
        };
        return this.safeString(ledgerType, type, type);
    }
    /**
     * @method
     * @name hyperliquid#fetchDeposits
     * @description fetch all deposits made to an account
     * @param {string} code unified currency code
     * @param {int} [since] the earliest time in ms to fetch deposits for
     * @param {int} [limit] the maximum number of deposits structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {int} [params.until] the latest time in ms to fetch withdrawals for
     * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
     */
    async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchDepositsWithdrawals', params);
        const request = {
            'type': 'userNonFundingLedgerUpdates',
            'user': userAddress,
        };
        if (since !== undefined) {
            request['startTime'] = since;
        }
        const until = this.safeInteger(params, 'until');
        if (until !== undefined) {
            request['endTime'] = until;
            params = this.omit(params, ['until']);
        }
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        // [
        //     {
        //         "time":1724762307531,
        //         "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
        //         "delta":{
        //             "type":"accountClassTransfer",
        //             "usdc":"50.0",
        //             "toPerp":false
        //         }
        //     }
        // ]
        //
        const records = this.extractTypeFromDelta(response);
        const deposits = this.filterByArray(records, 'type', ['deposit'], false);
        return this.parseTransactions(deposits, undefined, since, limit);
    }
    /**
     * @method
     * @name hyperliquid#fetchWithdrawals
     * @description fetch all withdrawals made from an account
     * @param {string} code unified currency code
     * @param {int} [since] the earliest time in ms to fetch withdrawals for
     * @param {int} [limit] the maximum number of withdrawals structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @param {int} [params.until] the latest time in ms to fetch withdrawals for
     * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
     */
    async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchDepositsWithdrawals', params);
        const request = {
            'type': 'userNonFundingLedgerUpdates',
            'user': userAddress,
        };
        if (since !== undefined) {
            request['startTime'] = since;
        }
        const until = this.safeInteger(params, 'until');
        if (until !== undefined) {
            request['endTime'] = until;
            params = this.omit(params, ['until']);
        }
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        // [
        //     {
        //         "time":1724762307531,
        //         "hash":"0x620a234a7e0eb7930575040f59482a01050058b0802163b4767bfd9033e77781",
        //         "delta":{
        //             "type":"accountClassTransfer",
        //             "usdc":"50.0",
        //             "toPerp":false
        //         }
        //     }
        // ]
        //
        const records = this.extractTypeFromDelta(response);
        const withdrawals = this.filterByArray(records, 'type', ['withdraw'], false);
        return this.parseTransactions(withdrawals, undefined, since, limit);
    }
    /**
     * @method
     * @name hyperliquid#fetchOpenInterests
     * @description Retrieves the open interest for a list of symbols
     * @param {string[]} [symbols] Unified CCXT market symbol
     * @param {object} [params] exchange specific parameters
     * @returns {object} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure}
     */
    async fetchOpenInterests(symbols = undefined, params = {}) {
        await this.loadMarkets();
        symbols = this.marketSymbols(symbols);
        const swapMarkets = await this.fetchSwapMarkets();
        return this.parseOpenInterests(swapMarkets, symbols);
    }
    /**
     * @method
     * @name hyperliquid#fetchOpenInterest
     * @description retrieves the open interest of a contract trading pair
     * @param {string} symbol unified CCXT market symbol
     * @param {object} [params] exchange specific parameters
     * @returns {object} an [open interest structure]{@link https://docs.ccxt.com/#/?id=open-interest-structure}
     */
    async fetchOpenInterest(symbol, params = {}) {
        symbol = this.symbol(symbol);
        await this.loadMarkets();
        const ois = await this.fetchOpenInterests([symbol], params);
        return ois[symbol];
    }
    parseOpenInterest(interest, market = undefined) {
        //
        //  {
        //      szDecimals: '2',
        //      name: 'HYPE',
        //      maxLeverage: '3',
        //      funding: '0.00014735',
        //      openInterest: '14677900.74',
        //      prevDayPx: '26.145',
        //      dayNtlVlm: '299643445.12560016',
        //      premium: '0.00081613',
        //      oraclePx: '27.569',
        //      markPx: '27.63',
        //      midPx: '27.599',
        //      impactPxs: [ '27.5915', '27.6319' ],
        //      dayBaseVlm: '10790652.83',
        //      baseId: 159
        //  }
        //
        interest = this.safeDict(interest, 'info', {});
        const coin = this.safeString(interest, 'name');
        let marketId = undefined;
        if (coin !== undefined) {
            marketId = this.coinToMarketId(coin);
        }
        return this.safeOpenInterest({
            'symbol': this.safeSymbol(marketId),
            'openInterestAmount': this.safeNumber(interest, 'openInterest'),
            'openInterestValue': undefined,
            'timestamp': undefined,
            'datetime': undefined,
            'info': interest,
        }, market);
    }
    /**
     * @method
     * @name hyperliquid#fetchFundingHistory
     * @description fetch the history of funding payments paid and received on this account
     * @param {string} [symbol] unified market symbol
     * @param {int} [since] the earliest time in ms to fetch funding history for
     * @param {int} [limit] the maximum number of funding history structures to retrieve
     * @param {object} [params] extra parameters specific to the exchange API endpoint
     * @returns {object} a [funding history structure]{@link https://docs.ccxt.com/#/?id=funding-history-structure}
     */
    async fetchFundingHistory(symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets();
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market(symbol);
        }
        let userAddress = undefined;
        [userAddress, params] = this.handlePublicAddress('fetchFundingHistory', params);
        const request = {
            'user': userAddress,
            'type': 'userFunding',
        };
        if (since !== undefined) {
            request['startTime'] = since;
        }
        const until = this.safeInteger(params, 'until');
        params = this.omit(params, 'until');
        if (until !== undefined) {
            request['endTime'] = until;
        }
        const response = await this.publicPostInfo(this.extend(request, params));
        //
        // [
        //     {
        //         "time": 1734026400057,
        //         "hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        //         "delta": {
        //             "type": "funding",
        //             "coin": "SOL",
        //             "usdc": "75.635093",
        //             "szi": "-7375.9",
        //             "fundingRate": "0.00004381",
        //             "nSamples": null
        //         }
        //     }
        // ]
        //
        return this.parseIncomes(response, market, since, limit);
    }
    parseIncome(income, market = undefined) {
        //
        // {
        //     "time": 1734026400057,
        //     "hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        //     "delta": {
        //         "type": "funding",
        //         "coin": "SOL",
        //         "usdc": "75.635093",
        //         "szi": "-7375.9",
        //         "fundingRate": "0.00004381",
        //         "nSamples": null
        //     }
        // }
        //
        const id = this.safeString(income, 'hash');
        const timestamp = this.safeInteger(income, 'time');
        const delta = this.safeDict(income, 'delta');
        const baseId = this.safeString(delta, 'coin');
        const marketSymbol = baseId + '/USDC:USDC';
        market = this.safeMarket(marketSymbol);
        const symbol = market['symbol'];
        const amount = this.safeString(delta, 'usdc');
        const code = this.safeCurrencyCode('USDC');
        const rate = this.safeNumber(delta, 'fundingRate');
        return {
            'info': income,
            'symbol': symbol,
            'code': code,
            'timestamp': timestamp,
            'datetime': this.iso8601(timestamp),
            'id': id,
            'amount': this.parseNumber(amount),
            'rate': rate,
        };
    }
    extractTypeFromDelta(data = []) {
        const records = [];
        for (let i = 0; i < data.length; i++) {
            const record = data[i];
            record['type'] = record['delta']['type'];
            records.push(record);
        }
        return records;
    }
    formatVaultAddress(address = undefined) {
        if (address === undefined) {
            return undefined;
        }
        if (address.startsWith('0x')) {
            return address.replace('0x', '');
        }
        return address;
    }
    handlePublicAddress(methodName, params) {
        let userAux = undefined;
        [userAux, params] = this.handleOptionAndParams(params, methodName, 'user');
        let user = userAux;
        [user, params] = this.handleOptionAndParams(params, methodName, 'address', userAux);
        if ((user !== undefined) && (user !== '')) {
            return [user, params];
        }
        if ((this.walletAddress !== undefined) && (this.walletAddress !== '')) {
            return [this.walletAddress, params];
        }
        throw new errors.ArgumentsRequired(this.id + ' ' + methodName + '() requires a user parameter inside \'params\' or the wallet address set');
    }
    coinToMarketId(coin) {
        if (coin.indexOf('/') > -1 || coin.indexOf('@') > -1) {
            return coin; // spot
        }
        return coin + '/USDC:USDC';
    }
    handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
        if (!response) {
            return undefined; // 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":"ok","response":{"type":"order","data":{"statuses":[{"error":"Insufficient margin to place order. asset=84"}]}}}
        //
        const status = this.safeString(response, 'status', '');
        const error = this.safeString(response, 'error');
        let message = undefined;
        if (status === 'err') {
            message = this.safeString(response, 'response');
        }
        else if (error !== undefined) {
            message = error;
        }
        else {
            const responsePayload = this.safeDict(response, 'response', {});
            const data = this.safeDict(responsePayload, 'data', {});
            const statuses = this.safeList(data, 'statuses', []);
            const firstStatus = this.safeDict(statuses, 0);
            message = this.safeString(firstStatus, 'error');
        }
        const feedback = this.id + ' ' + body;
        const nonEmptyMessage = ((message !== undefined) && (message !== ''));
        if (nonEmptyMessage) {
            this.throwExactlyMatchedException(this.exceptions['exact'], message, feedback);
            this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
        }
        if (nonEmptyMessage) {
            throw new errors.ExchangeError(feedback); // unknown message
        }
        return undefined;
    }
    sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const url = this.implodeHostname(this.urls['api'][api]) + '/' + path;
        if (method === 'POST') {
            headers = {
                'Content-Type': 'application/json',
            };
            body = this.json(params);
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }
    calculateRateLimiterCost(api, method, path, params, config = {}) {
        if (('byType' in config) && ('type' in params)) {
            const type = params['type'];
            const byType = config['byType'];
            if (type in byType) {
                return byType[type];
            }
        }
        return this.safeValue(config, 'cost', 1);
    }
    parseCreateEditOrderArgs(id, symbol, type, side, amount, price = undefined, params = {}) {
        const market = this.market(symbol);
        let vaultAddress = undefined;
        [vaultAddress, params] = this.handleOptionAndParams(params, 'createOrder', 'vaultAddress');
        vaultAddress = this.formatVaultAddress(vaultAddress);
        symbol = market['symbol'];
        const order = {
            'symbol': symbol,
            'type': type,
            'side': side,
            'amount': amount,
            'price': price,
            'params': params,
        };
        const globalParams = {};
        if (vaultAddress !== undefined) {
            globalParams['vaultAddress'] = vaultAddress;
        }
        if (id !== undefined) {
            order['id'] = id;
        }
        return [order, globalParams];
    }
}

module.exports = hyperliquid;