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

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

from ccxt.async_support.base.exchange import Exchange
from ccxt.abstract.lighter import ImplicitAPI
from ccxt.base.types import Account, Any, Balances, Currencies, Currency, Int, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, Transaction, TransferEntry
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class lighter(Exchange, ImplicitAPI):

    def describe(self) -> Any:
        return self.deep_extend(super(lighter, self).describe(), {
            'id': 'lighter',
            'name': 'Lighter',
            'countries': [],
            'version': 'v1',
            'rateLimit': 1000,  # 60 requests per minute - normal account
            'certified': False,
            'pro': True,
            'dex': True,
            'has': {
                'CORS': None,
                'spot': False,
                'margin': False,
                'swap': True,
                'future': False,
                'option': False,
                'addMargin': True,
                'borrowCrossMargin': False,
                'borrowIsolatedMargin': False,
                'borrowMargin': False,
                'cancelAllOrders': True,
                'cancelAllOrdersAfter': True,
                'cancelOrder': True,
                'cancelOrders': False,
                'cancelOrdersForSymbols': False,
                'closeAllPositions': False,
                'closePosition': False,
                'createMarketBuyOrderWithCost': False,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createOrders': False,
                'createPostOnlyOrder': False,
                'createReduceOnlyOrder': False,
                'createStopOrder': False,
                'createTriggerOrder': False,
                'editOrder': True,
                'fetchAccounts': True,
                'fetchAllGreeks': False,
                'fetchBalance': True,
                'fetchBorrowInterest': False,
                'fetchBorrowRate': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchBorrowRatesPerSymbol': False,
                'fetchCanceledAndClosedOrders': False,
                'fetchCanceledOrders': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': False,
                'fetchDepositAddresses': False,
                'fetchDeposits': True,
                'fetchDepositWithdrawFee': False,
                'fetchDepositWithdrawFees': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': True,
                'fetchGreeks': False,
                'fetchIndexOHLCV': False,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLedger': False,
                'fetchLeverage': False,
                'fetchLeverageTiers': False,
                'fetchLiquidations': False,
                'fetchMarginMode': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyLiquidations': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': False,
                'fetchOpenInterestHistory': False,
                'fetchOpenInterests': False,
                'fetchOpenOrders': True,
                'fetchOption': False,
                'fetchOptionChain': False,
                'fetchOrder': False,
                'fetchOrderBook': True,
                'fetchOrders': False,
                'fetchOrderTrades': False,
                'fetchPosition': True,
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': False,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTransfer': False,
                'fetchTransfers': True,
                'fetchVolatilityHistory': False,
                'fetchWithdrawal': False,
                'fetchWithdrawals': True,
                'reduceMargin': True,
                'repayCrossMargin': False,
                'repayIsolatedMargin': False,
                'sandbox': True,
                'setLeverage': True,
                'setMargin': True,
                'setMarginMode': True,
                'setPositionMode': False,
                'transfer': True,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '4h': '4h',
                '12h': '12h',
                '1d': '1d',
                '1w': '1w',
            },
            'hostname': 'zklighter.elliot.ai',
            'urls': {
                'logo': 'https://github.com/user-attachments/assets/ff1aaf96-bffb-4545-a750-5eba716e75d0',
                'api': {
                    'root': 'https://mainnet.{hostname}',
                    'public': 'https://mainnet.{hostname}',
                    'private': 'https://mainnet.{hostname}',
                },
                'test': {
                    'root': 'https://testnet.{hostname}',
                    'public': 'https://testnet.{hostname}',
                    'private': 'https://testnet.{hostname}',
                },
                'www': 'https://lighter.xyz/',
                'doc': 'https://apidocs.lighter.xyz/',
                'fees': 'https://docs.lighter.xyz/perpetual-futures/fees',
                'referral': {
                    'url': 'app.lighter.xyz/?referral=715955W9',
                    'discount': 0.1,  # user gets 10% of the points
                },
            },
            'api': {
                'root': {
                    'get': {
                        # root
                        '': 1,  # status
                        'info': 1,
                    },
                },
                'public': {
                    'get': {
                        # account
                        'account': 1,
                        'accountsByL1Address': 1,
                        'apikeys': 1,
                        # order
                        'exchangeStats': 1,
                        'assetDetails': 1,
                        'orderBookDetails': 1,
                        'orderBookOrders': 1,
                        'orderBooks': 1,
                        'recentTrades': 1,
                        # transaction
                        'blockTxs': 1,
                        'nextNonce': 1,
                        'tx': 1,
                        'txFromL1TxHash': 1,
                        'txs': 1,
                        # announcement
                        'announcement': 1,
                        # block
                        'block': 1,
                        'blocks': 1,
                        'currentHeight': 1,
                        # candlestick
                        'candles': 1,
                        'fundings': 1,
                        # bridge
                        'fastbridge/info': 1,
                        # funding
                        'funding-rates': 1,
                        # info
                        'withdrawalDelay': 1,
                    },
                    'post': {
                        # transaction
                        'sendTx': 1,
                        'sendTxBatch': 1,
                    },
                },
                'private': {
                    'get': {
                        # account
                        'accountLimits': 1,
                        'accountMetadata': 1,
                        'pnl': 1,
                        'l1Metadata': 1,
                        'liquidations': 1,
                        'positionFunding': 1,
                        'publicPoolsMetadata': 1,
                        # order
                        'accountActiveOrders': 1,
                        'accountInactiveOrders': 1,
                        'export': 1,
                        'trades': 1,
                        # transaction
                        'accountTxs': 1,
                        'deposit/history': 1,
                        'transfer/history': 1,
                        'withdraw/history': 1,
                        # referral
                        'referral/points': 1,
                        # info
                        'transferFeeInfo': 1,
                    },
                    'post': {
                        # account
                        'changeAccountTier': 1,
                        # notification
                        'notification/ack': 1,
                    },
                },
            },
            'httpExceptions': {
            },
            'exceptions': {
                'exact': {
                    '21500': ExchangeError,  # transaction not found
                    '21501': ExchangeError,  # invalid tx info
                    '21502': ExchangeError,  # marshal tx failed
                    '21503': ExchangeError,  # marshal event failed
                    '21504': ExchangeError,  # fail to l1 signature
                    '21505': ExchangeError,  # unsupported tx type
                    '21506': ExchangeError,  # too many pending txs. Please try again later
                    '21507': ExchangeError,  # account is below maintenance margin, can't execute transaction
                    '21508': ExchangeError,  # account is below initial margin, can't execute transaction
                    '21511': ExchangeError,  # invalid tx type for account
                    '21512': ExchangeError,  # invalid l1 request id
                    '21600': InvalidOrder,  # given order is not an active limit order
                    '21601': InvalidOrder,  # order book is full
                    '21602': InvalidOrder,  # invalid market index
                    '21603': InvalidOrder,  # invalid min amounts for market
                    '21604': InvalidOrder,  # invalid margin fractions for market
                    '21605': InvalidOrder,  # invalid market status
                    '21606': InvalidOrder,  # market already exist for given index
                    '21607': InvalidOrder,  # invalid market fees
                    '21608': InvalidOrder,  # invalid quote multiplier
                    '21611': InvalidOrder,  # invalid interest rate
                    '21612': InvalidOrder,  # invalid open interest
                    '21613': InvalidOrder,  # invalid margin mode
                    '21614': InvalidOrder,  # no position found
                    '21700': InvalidOrder,  # invalid order index
                    '21701': InvalidOrder,  # invalid base amount
                    '21702': InvalidOrder,  # invalid price
                    '21703': InvalidOrder,  # invalid isAsk
                    '21704': InvalidOrder,  # invalid OrderType
                    '21705': InvalidOrder,  # invalid OrderTimeInForce
                    '21706': InvalidOrder,  # invalid order base or quote amount
                    '21707': InvalidOrder,  # account is not owner of the order
                    '21708': InvalidOrder,  # order is empty
                    '21709': InvalidOrder,  # order is inactive
                    '21710': InvalidOrder,  # unsupported order type
                    '21711': InvalidOrder,  # invalid expiry
                    '21712': InvalidOrder,  # account has a queued cancel all orders request
                    '21713': InvalidOrder,  # invalid cancel all time in force
                    '21714': InvalidOrder,  # invalid cancel all time
                    '21715': InvalidOrder,  # given order is not an active order
                    '21716': InvalidOrder,  # order is not expired
                    '21717': InvalidOrder,  # maximum active limit order count reached
                    '21718': InvalidOrder,  # maximum active limit order count per market reached
                    '21719': InvalidOrder,  # maximum pending order count reached
                    '21720': InvalidOrder,  # maximum pending order count per market reached
                    '21721': InvalidOrder,  # maximum twap order count reached
                    '21722': InvalidOrder,  # maximum conditional order count reached
                    '21723': InvalidOrder,  # invalid account health
                    '21724': InvalidOrder,  # invalid liquidation size
                    '21725': InvalidOrder,  # invalid liquidation price
                    '21726': InvalidOrder,  # insurance fund cannot be partially liquidated
                    '21727': InvalidOrder,  # invalid client order index
                    '21728': InvalidOrder,  # client order index already exists
                    '21729': InvalidOrder,  # invalid order trigger price
                    '21730': InvalidOrder,  # order status is not pending
                    '21731': InvalidOrder,  # order can not be triggered
                    '21732': InvalidOrder,  # reduce only increases position
                    '21733': InvalidOrder,  # order price flagged accidental price
                    '21734': InvalidOrder,  # limit order price is too far from the mark price
                    '21735': InvalidOrder,  # SL/TP order price is too far from the trigger price
                    '21736': InvalidOrder,  # invalid order trigger status
                    '21737': InvalidOrder,  # invalid order status
                    '21738': InvalidOrder,  # invalid reduce only direction
                    '21739': InvalidOrder,  # not enough margin to create the order
                    '21740': InvalidOrder,  # invalid reduce only mode
                    '21901': InvalidOrder,  # deleverage against itself
                    '21902': InvalidOrder,  # deleverage does not match liquidation status
                    '21903': InvalidOrder,  # deleverage with open orders
                    '21904': InvalidOrder,  # invalid deleverage size
                    '21905': InvalidOrder,  # invalid deleverage price
                    '21906': InvalidOrder,  # invalid deleverage side
                    '23000': RateLimitExceeded,  # Too Many Requests!
                    '23001': RateLimitExceeded,  # Too Many Subscriptions!
                    '23002': RateLimitExceeded,  # Too Many Different Accounts!
                    '23003': RateLimitExceeded,  # Too Many Connections!
                },
                'broad': {
                },
            },
            'fees': {
                'taker': 0,
                'maker': 0,
            },
            'requiredCredentials': {
                'apiKey': False,
                'secret': False,
                'walletAddress': False,
                'privateKey': True,
                'password': False,
            },
            'precisionMode': TICK_SIZE,
            'commonCurrencies': {},
            'options': {
                'defaultType': 'swap',
                'chainId': 304,
                'accountIndex': None,
                'apiKeyIndex': None,
                'wasmExecPath': None,  # [JS Only] users should set the path to wasm_exec.js. It can be downloaded here https://github.com/ccxt/lighter-wasm
                'libraryPath': None,  # users should set the path to the lighter signing library. It can be downloaded here https://github.com/elliottech/lighter-python/tree/main/lighter/signers, GO users don't need it
            },
            'features': {
                'default': {
                    'sandbox': True,
                    'createOrder': {
                        'timeInForce': {
                            'IOC': True,
                            'FOK': True,
                            'PO': True,
                            'GTD': False,
                        },
                        'leverage': False,
                        'marketBuyRequiresPrice': False,
                        'marketBuyByCost': False,
                        'selfTradePrevention': False,
                        'trailing': False,
                        'iceberg': False,
                    },
                },
            },
        })

    async def load_account(self, chainId, privateKey, apiKeyIndex, accountIndex, params={}):
        signer = self.safe_dict(self.options, 'signer')
        if signer is not None:
            return signer
        libraryPath = None
        libraryPath, params = self.handle_option_and_params(params, 'loadAccount', 'libraryPath')
        signer = await self.load_lighter_library(libraryPath, chainId, privateKey, apiKeyIndex, accountIndex)
        self.options['signer'] = signer
        return signer

    async def pre_load_lighter_library(self, params={}):
        """
        if the required credentials are available in options, it will pre-load the lighter Signer to avoid delaying sensitive calls like createOrder the first time they're executed
 @param params
        :returns boolean: True if the signer was loaded, False otherwise
        """
        signer = self.safe_dict(self.options, 'signer')
        if signer is not None:
            return True
        libraryPath = None
        libraryPath, params = self.handle_option_and_params(params, 'loadAccount', 'libraryPath')
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'loadAccount', 'apiKeyIndex', 'api_key_index')
        accountIndex = None
        accountIndex, params = self.handle_option_and_params_2(params, 'loadAccount', 'accountIndex', 'account_index')
        privateKeyIsSet = (self.privateKey is not None) and (self.privateKey != '')
        if privateKeyIsSet and (libraryPath is not None) and (apiKeyIndex is not None) and (accountIndex is not None):
            signer = await self.load_lighter_library(libraryPath, self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex)
            self.options['signer'] = signer
            return True
        return False

    async def handle_account_index(self, params: object, methodName1: str, optionName1: str, optionName2: str, defaultValue=None):
        accountIndex = None
        accountIndex, params = self.handle_option_and_params_2(params, methodName1, optionName1, optionName2, defaultValue)
        if accountIndex is None:
            walletAddress = self.walletAddress
            if walletAddress is None or walletAddress == '':
                raise ArgumentsRequired(self.id + ' ' + methodName1 + '() requires an ' + optionName1 + '/' + optionName2 + ' parameter or walletAddress to fetch accountIndex')
            res = await self.publicGetAccountsByL1Address({'l1_address': walletAddress})
            #
            # {
            #     "code": 200,
            #     "l1_address": "0xaaaabbbb....ccccdddd",
            #     "sub_accounts": [
            #         {
            #             "code": 0,
            #             "account_type": 0,
            #             "index": 666666,
            #             "l1_address": "0xaaaabbbb....ccccdddd",
            #             "cancel_all_time": 0,
            #             "total_order_count": 0,
            #             "total_isolated_order_count": 0,
            #             "pending_order_count": 0,
            #             "available_balance": "",
            #             "status": 0,
            #             "collateral": "40",
            #             "transaction_time": 0,
            #             "account_trading_mode": 0
            #         }
            #     ]
            # }
            #
            subAccounts = self.safe_list(res, 'sub_accounts')
            if isinstance(subAccounts, list):
                account = self.safe_dict(subAccounts, 0)
                if account is None:
                    raise ArgumentsRequired(self.id + ' ' + methodName1 + '() requires an ' + optionName1 + ' or ' + optionName2 + ' parameter')
                accountIndex = account['index']
                self.options['accountIndex'] = accountIndex
        return [self.parse_to_int(accountIndex), params]

    async def create_sub_account(self, name: str, params={}):
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'createSubAccount', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' createSubAccount() requires an apiKeyIndex parameter')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'createSubAccount', 'accountIndex', 'account_index')
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex)
        signRaw: dict = {
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_create_sub_account(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        return await self.publicPostSendTx(request)

    def create_auth(self, params={}):
        # don't omit [accountIndex, apiKeyIndex], request may need them
        apiKeyIndex = self.safe_integer_2(params, 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            res = self.handle_option_and_params_2({}, 'createAuth', 'apiKeyIndex', 'api_key_index')
            apiKeyIndex = self.safe_integer(res, 0)
        accountIndex = self.safe_integer_2(params, 'accountIndex', 'account_index')
        if accountIndex is None:
            res = self.handle_option_and_params_2({}, 'createAuth', 'accountIndex', 'account_index')
            accountIndex = self.safe_integer(res, 0)
        rs = {
            'deadline': self.seconds() + 60,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        return self.lighter_create_auth_token(self.safe_value(self.options, 'signer'), rs)

    def pow(self, n: str, m: str):
        r = Precise.string_mul(n, '1')
        c = self.parse_to_int(m)
        if c < 0:
            raise BadRequest(self.id + ' pow() requires m > 0.')
        if c == 0:
            return '1'
        if c > 100:
            raise BadRequest(self.id + ' pow() requires m < 100.')
        for i in range(1, c):
            r = Precise.string_mul(r, n)
        return r

    def set_sandbox_mode(self, enable: bool):
        super(lighter, self).set_sandbox_mode(enable)
        self.options['sandboxMode'] = enable
        self.options['chainId'] = 300

    def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> List[Any]:
        """
 @ignore
        helper function to build the request
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much you want to trade in units of the base currency
        :param float [price]: the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.nonce]: nonce for the account
        :param int [params.apiKeyIndex]: apiKeyIndex
        :param int [params.accountIndex]: accountIndex
        :param int [params.orderExpiry]: orderExpiry
        :returns any[]: request to be sent to the exchange
        """
        if price is None:
            raise ArgumentsRequired(self.id + ' createOrder() requires a price argument')
        reduceOnly = self.safe_bool_2(params, 'reduceOnly', 'reduce_only', False)  # default False
        orderType = type.upper()
        market = self.market(symbol)
        orderSide = side.upper()
        request: dict = {
            'market_index': self.parse_to_int(market['id']),
        }
        nonce = None
        apiKeyIndex = None
        accountIndex = None
        orderExpiry = None
        apiKeyIndex, params = self.handle_option_and_params(params, 'createOrder', 'apiKeyIndex', 255)
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' createOrder() requires an apiKeyIndex parameter')
        accountIndex, params = self.handle_option_and_params_2(params, 'createOrder', 'accountIndex', 'account_index')
        nonce, params = self.handle_option_and_params(params, 'createOrder', 'nonce')
        orderExpiry, params = self.handle_option_and_params(params, 'createOrder', 'orderExpiry', 0)
        request['nonce'] = nonce
        request['api_key_index'] = apiKeyIndex
        request['account_index'] = self.parse_to_int(accountIndex)
        triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        stopLoss = self.safe_value(params, 'stopLoss')
        takeProfit = self.safe_value(params, 'takeProfit')
        hasStopLoss = (stopLoss is not None)
        hasTakeProfit = (takeProfit is not None)
        isConditional = (stopLossPrice or takeProfitPrice)
        isMarketOrder = (orderType == 'MARKET')
        timeInForce = self.safe_string_lower(params, 'timeInForce', 'gtt')
        postOnly = self.is_post_only(isMarketOrder, None, params)
        params = self.omit(params, ['stopLoss', 'takeProfit', 'timeInForce'])
        orderTypeNum = None
        timeInForceNum = None
        if isMarketOrder:
            orderTypeNum = 1
            timeInForceNum = 0
        else:
            orderTypeNum = 0
        if orderSide == 'BUY':
            request['is_ask'] = 0
        else:
            request['is_ask'] = 1
        if postOnly:
            timeInForceNum = 2
        else:
            if not isMarketOrder:
                if timeInForce == 'ioc':
                    timeInForceNum = 0
                    orderExpiry = 0
                elif timeInForce == 'gtt':
                    timeInForceNum = 1
                    orderExpiry = -1
        marketInfo = self.safe_dict(market, 'info')
        amountStr = None
        priceStr = self.price_to_precision(symbol, price)
        amountScale = self.pow('10', marketInfo['size_decimals'])
        priceScale = self.pow('10', marketInfo['price_decimals'])
        triggerPriceStr = '0'  # default is 0
        defaultClientOrderId = self.rand_number(9)  # c# only support int32 2147483647.
        clientOrderId = self.safe_integer_2(params, 'client_order_index', 'clientOrderId', defaultClientOrderId)
        params = self.omit(params, ['reduceOnly', 'reduce_only', 'timeInForce', 'postOnly', 'nonce', 'apiKeyIndex', 'stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'client_order_index', 'clientOrderId'])
        if isConditional:
            amountStr = self.number_to_string(amount)
            if stopLossPrice is not None:
                if isMarketOrder:
                    orderTypeNum = 2
                else:
                    orderTypeNum = 3
                triggerPriceStr = self.price_to_precision(symbol, stopLossPrice)
            elif takeProfitPrice is not None:
                if isMarketOrder:
                    orderTypeNum = 4
                else:
                    orderTypeNum = 5
                triggerPriceStr = self.price_to_precision(symbol, takeProfitPrice)
        else:
            amountStr = self.amount_to_precision(symbol, amount)
        request['order_expiry'] = orderExpiry
        request['order_type'] = orderTypeNum
        request['time_in_force'] = timeInForceNum
        request['reduce_only'] = 1 if (reduceOnly) else 0
        request['client_order_index'] = clientOrderId
        request['base_amount'] = self.parse_to_int(Precise.string_mul(amountStr, amountScale))
        request['avg_execution_price'] = self.parse_to_int(Precise.string_mul(priceStr, priceScale))
        request['trigger_price'] = self.parse_to_int(Precise.string_mul(triggerPriceStr, priceScale))
        orders = []
        orders.append(self.extend(request, params))
        if hasStopLoss or hasTakeProfit:
            # group order
            orders[0]['client_order_index'] = 0  # client order index should be 0
            triggerOrderSide = ''
            if side == 'BUY':
                triggerOrderSide = 'sell'
            else:
                triggerOrderSide = 'buy'
            stopLossOrderTriggerPrice = self.safe_number_n(stopLoss, ['triggerPrice', 'stopPrice'])
            stopLossOrderType = self.safe_string(stopLoss, 'type', 'limit')
            stopLossOrderLimitPrice = self.safe_number_n(stopLoss, ['price', 'stopLossPrice'], stopLossOrderTriggerPrice)
            takeProfitOrderTriggerPrice = self.safe_number_n(takeProfit, ['triggerPrice', 'stopPrice'])
            takeProfitOrderType = self.safe_string(takeProfit, 'type', 'limit')
            takeProfitOrderLimitPrice = self.safe_number_n(takeProfit, ['price', 'takeProfitPrice'], takeProfitOrderTriggerPrice)
            # amount should be 0 for child orders
            if stopLoss is not None:
                orderObj = self.create_order_request(symbol, stopLossOrderType, triggerOrderSide, 0, stopLossOrderLimitPrice, self.extend(params, {
                    'stopLossPrice': stopLossOrderTriggerPrice,
                    'reduceOnly': True,
                }))[0]
                orderObj['client_order_index'] = 0
                orders.append(orderObj)
            if takeProfit is not None:
                orderObj = self.create_order_request(symbol, takeProfitOrderType, triggerOrderSide, 0, takeProfitOrderLimitPrice, self.extend(params, {
                    'takeProfitPrice': takeProfitOrderTriggerPrice,
                    'reduceOnly': True,
                }))[0]
                orderObj['client_order_index'] = 0
                orders.append(orderObj)
        return orders

    async def fetch_nonce(self, accountIndex, apiKeyIndex, params={}):
        if (accountIndex is None) or (apiKeyIndex is None):
            raise ArgumentsRequired(self.id + ' fetchNonce() requires accountIndex and apiKeyIndex.')
        if 'nonce' in params:
            return self.safe_integer(params, 'nonce')
        nonceInOptions = self.safe_integer(self.options, 'nonce')
        if nonceInOptions is not None:
            return nonceInOptions
        response = await self.publicGetNextNonce({'account_index': accountIndex, 'api_key_index': apiKeyIndex})
        return self.safe_integer(response, 'nonce')

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.timeInForce]: 'GTT' or 'IOC', default is 'GTT'
        :param int [params.clientOrderId]: client order id, should be unique for each order, default is a random number
        :param str [params.triggerPrice]: trigger price for stop loss or take profit orders, in units of the quote currency
        :param boolean [params.reduceOnly]: whether the order is reduce only, default False
        :param int [params.nonce]: nonce for the account
        :param int [params.apiKeyIndex]: apiKeyIndex
        :param int [params.accountIndex]: accountIndex
        :param int [params.orderExpiry]: orderExpiry
        :returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
        """
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'createOrder', 'accountIndex', 'account_index')
        params['accountIndex'] = accountIndex
        market = self.market(symbol)
        groupingType = None
        groupingType, params = self.handle_option_and_params(params, 'createOrder', 'groupingType', 3)  # default GROUPING_TYPE_ONE_TRIGGERS_A_ONE_CANCELS_THE_OTHER
        orderRequests = self.create_order_request(symbol, type, side, amount, price, params)
        # for php
        totalOrderRequests = len(orderRequests)
        apiKeyIndex = None
        order = None
        if totalOrderRequests > 0:
            order = orderRequests[0]
            apiKeyIndex = order['api_key_index']
            if order['nonce'] is None:
                nonceInOptions = self.safe_integer(self.options, 'nonce')
                if nonceInOptions is not None:
                    order['nonce'] = nonceInOptions
                else:
                    order['nonce'] = await self.fetch_nonce(accountIndex, apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType = None
        txInfo = None
        if totalOrderRequests < 2:
            txType, txInfo = self.lighter_sign_create_order(signer, order)
        else:
            signingPayload = {
                'grouping_type': groupingType,
                'orders': orderRequests,
                'nonce': order['nonce'],
                'api_key_index': apiKeyIndex,
                'account_index': accountIndex,
            }
            txType, txInfo = self.lighter_sign_create_grouped_orders(signer, signingPayload)
        request = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        response = await self.publicPostSendTx(request)
        #
        # {
        #     "code": 200,
        #     "message": "{\"ratelimit\": \"didn't use volume quota\"}",
        #     "tx_hash": "txhash",
        #     "predicted_execution_time_ms": 1766088500120
        # }
        #
        return self.parse_order(self.deep_extend(response, order), market)

    async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order:
        """
        cancels an order and places a new order
        :param str id: order id
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of the currency you want to trade in units of the base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param str [params.apiKeyIndex]: api key index
        :returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
        """
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'editOrder', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' editOrder() requires an apiKeyIndex parameter')
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'editOrder', 'accountIndex', 'account_index')
        market = self.market(symbol)
        marketInfo = self.safe_dict(market, 'info')
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex)
        amountScale = self.pow('10', marketInfo['size_decimals'])
        priceScale = self.pow('10', marketInfo['price_decimals'])
        triggerPrice = self.safe_string_n(params, ['stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice'])
        params = self.omit(params, ['stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice'])
        amountStr = None
        priceStr = self.price_to_precision(symbol, price)
        triggerPriceStr = '0'  # default is 0
        if triggerPrice is not None:
            amountStr = self.number_to_string(amount)
            triggerPriceStr = self.price_to_precision(symbol, triggerPrice)
        else:
            amountStr = self.amount_to_precision(symbol, amount)
        signRaw: dict = {
            'market_index': self.parse_to_int(market['id']),
            'index': self.parse_to_int(id),
            'base_amount': self.parse_to_int(Precise.string_mul(amountStr, amountScale)),
            'price': self.parse_to_int(Precise.string_mul(priceStr, priceScale)),
            'trigger_price': self.parse_to_int(Precise.string_mul(triggerPriceStr, priceScale)),
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_modify_order(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        response = await self.publicPostSendTx(request)
        return self.parse_order(response, market)

    async def fetch_status(self, params={}):
        """
        the latest known information on the availability of the exchange API

        https://apidocs.lighter.xyz/reference/status

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `status structure <https://docs.ccxt.com/#/?id=exchange-status-structure>`
        """
        response = await self.rootGet(params)
        #
        #     {
        #         "status": "1",
        #         "network_id": "1",
        #         "timestamp": "1717777777"
        #     }
        #
        status = self.safe_string(response, 'status')
        return {
            'status': 'ok' if (status == '200') else 'error',  # if there's no Errors, status = 'ok'
            'updated': None,
            'eta': None,
            'url': None,
            'info': response,
        }

    async def fetch_time(self, params={}) -> Int:
        """
        fetches the current integer timestamp in milliseconds from the exchange server

        https://apidocs.lighter.xyz/reference/status

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = await self.rootGet(params)
        #
        #     {
        #         "status": "1",
        #         "network_id": "1",
        #         "timestamp": "1717777777"
        #     }
        #
        return self.safe_timestamp(response, 'timestamp')

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for lighter

        https://apidocs.lighter.xyz/reference/orderbookdetails

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = await self.publicGetOrderBookDetails(params)
        #
        #     {
        #         "code": 200,
        #         "order_book_details": [
        #             {
        #                 "symbol": "ETH",
        #                 "market_id": 0,
        #                 "status": "active",
        #                 "taker_fee": "0.0000",
        #                 "maker_fee": "0.0000",
        #                 "liquidation_fee": "1.0000",
        #                 "min_base_amount": "0.0050",
        #                 "min_quote_amount": "10.000000",
        #                 "order_quote_limit": "",
        #                 "supported_size_decimals": 4,
        #                 "supported_price_decimals": 2,
        #                 "supported_quote_decimals": 6,
        #                 "size_decimals": 4,
        #                 "price_decimals": 2,
        #                 "quote_multiplier": 1,
        #                 "default_initial_margin_fraction": 500,
        #                 "min_initial_margin_fraction": 200,
        #                 "maintenance_margin_fraction": 120,
        #                 "closeout_margin_fraction": 80,
        #                 "last_trade_price": 3550.69,
        #                 "daily_trades_count": 1197349,
        #                 "daily_base_token_volume": 481297.3509,
        #                 "daily_quote_token_volume": 1671431095.263844,
        #                 "daily_price_low": 3402.41,
        #                 "daily_price_high": 3571.45,
        #                 "daily_price_change": 0.5294300840859545,
        #                 "open_interest": 39559.3278,
        #                 "daily_chart": {},
        #                 "market_config": {
        #                     "market_margin_mode": 0,
        #                     "insurance_fund_account_index": 281474976710655,
        #                     "liquidation_mode": 0,
        #                     "force_reduce_only": False,
        #                     "trading_hours": ""
        #                 }
        #             }
        #         ]
        #     }
        #
        markets = self.safe_list(response, 'order_book_details', [])
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'market_id')
            baseId = self.safe_string(market, 'symbol')
            quoteId = 'USDC'
            settleId = 'USDC'
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = self.safe_currency_code(settleId)
            amountDecimals = self.safe_string_2(market, 'size_decimals', 'supported_size_decimals')
            priceDecimals = self.safe_string_2(market, 'price_decimals', 'supported_price_decimals')
            amountPrecision = None if (amountDecimals is None) else self.parse_number(self.parse_precision(amountDecimals))
            pricePrecision = None if (priceDecimals is None) else self.parse_number(self.parse_precision(priceDecimals))
            quoteMultiplier = self.safe_number(market, 'quote_multiplier')
            result.append({
                'id': id,
                'symbol': base + '/' + quote + ':' + settle,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': 'swap',
                'spot': False,
                'margin': False,
                'swap': True,
                'future': False,
                'option': False,
                'active': self.safe_string(market, 'status') == 'active',
                'contract': True,
                'linear': True,
                'inverse': False,
                'taker': self.safe_number(market, 'taker_fee'),
                'maker': self.safe_number(market, 'maker_fee'),
                'contractSize': quoteMultiplier,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': amountPrecision,
                    'price': pricePrecision,
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': self.safe_number(market, 'min_base_amount'),
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': self.safe_number(market, 'min_quote_amount'),
                        'max': None,
                    },
                },
                'created': None,
                'info': market,
            })
        return result

    async def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange

        https://apidocs.lighter.xyz/reference/assetdetails

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = await self.publicGetAssetDetails(params)
        await self.pre_load_lighter_library()
        #
        #     {
        #         "code": 200,
        #         "asset_details": [
        #             {
        #                 "asset_id": 3,
        #                 "symbol": "USDC",
        #                 "l1_decimals": 6,
        #                 "decimals": 6,
        #                 "min_transfer_amount": "1.000000",
        #                 "min_withdrawal_amount": "1.000000",
        #                 "margin_mode": "enabled",
        #                 "index_price": "1.000000",
        #                 "l1_address": "0x95Fd23d5110f9D89A4b0B7d63D78F5B5Ea5074D1"
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'asset_details', [])
        result: dict = {}
        for i in range(0, len(data)):
            entry = data[i]
            id = self.safe_string(entry, 'asset_id')
            code = self.safe_currency_code(self.safe_string(entry, 'symbol'))
            decimals = self.safe_string(entry, 'decimals')
            isUSDC = (code == 'USDC')
            depositMin = None
            withdrawMin = None
            if isUSDC:
                depositMin = self.safe_number(entry, 'min_transfer_amount')
                withdrawMin = self.safe_number(entry, 'min_withdrawal_amount')
            result[code] = self.safe_currency_structure({
                'id': id,
                'name': code,
                'code': code,
                'precision': self.parse_number('1e-' + decimals),
                'active': True,
                'fee': None,
                'networks': {},
                'deposit': isUSDC,
                'withdraw': isUSDC,
                'type': 'crypto',
                'limits': {
                    'deposit': {
                        'min': depositMin,
                        'max': None,
                    },
                    'withdraw': {
                        'min': withdrawMin,
                        'max': None,
                    },
                },
                'info': entry,
            })
        return result

    async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data

        https://apidocs.lighter.xyz/reference/orderbookorders

        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrderBook() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market_id': market['id'],
            'limit': 100,
        }
        if limit is not None:
            request['limit'] = min(limit, 100)
        response = await self.publicGetOrderBookOrders(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "total_asks": 1,
        #         "asks": [
        #             {
        #                 "order_index": 281475565888172,
        #                 "order_id": "281475565888172",
        #                 "owner_account_index": 134436,
        #                 "initial_base_amount": "0.2000",
        #                 "remaining_base_amount": "0.2000",
        #                 "price": "3430.00",
        #                 "order_expiry": 1765419046807
        #             }
        #         ],
        #         "total_bids": 1,
        #         "bids": [
        #             {
        #                 "order_index": 562949401225099,
        #                 "order_id": "562949401225099",
        #                 "owner_account_index": 314236,
        #                 "initial_base_amount": "1.7361",
        #                 "remaining_base_amount": "1.3237",
        #                 "price": "3429.80",
        #                 "order_expiry": 1765419047587
        #             }
        #         ]
        #     }
        #
        result = self.parse_order_book(response, market['symbol'], None, 'bids', 'asks', 'price', 'remaining_base_amount')
        return result

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        # fetchTicker, fetchTickers
        #     {
        #         "symbol": "ETH",
        #         "market_id": 0,
        #         "status": "active",
        #         "taker_fee": "0.0000",
        #         "maker_fee": "0.0000",
        #         "liquidation_fee": "1.0000",
        #         "min_base_amount": "0.0050",
        #         "min_quote_amount": "10.000000",
        #         "order_quote_limit": "",
        #         "supported_size_decimals": 4,
        #         "supported_price_decimals": 2,
        #         "supported_quote_decimals": 6,
        #         "size_decimals": 4,
        #         "price_decimals": 2,
        #         "quote_multiplier": 1,
        #         "default_initial_margin_fraction": 500,
        #         "min_initial_margin_fraction": 200,
        #         "maintenance_margin_fraction": 120,
        #         "closeout_margin_fraction": 80,
        #         "last_trade_price": 3550.69,
        #         "daily_trades_count": 1197349,
        #         "daily_base_token_volume": 481297.3509,
        #         "daily_quote_token_volume": 1671431095.263844,
        #         "daily_price_low": 3402.41,
        #         "daily_price_high": 3571.45,
        #         "daily_price_change": 0.5294300840859545,
        #         "open_interest": 39559.3278,
        #         "daily_chart": {},
        #         "market_config": {
        #             "market_margin_mode": 0,
        #             "insurance_fund_account_index": 281474976710655,
        #             "liquidation_mode": 0,
        #             "force_reduce_only": False,
        #             "trading_hours": ""
        #         }
        #     }
        #
        # watchTicker, watchTickers
        #     {
        #         "market_id": 0,
        #         "index_price": "3015.56",
        #         "mark_price": "3013.91",
        #         "open_interest": "122736286.659423",
        #         "open_interest_limit": "72057594037927936.000000",
        #         "funding_clamp_small": "0.0500",
        #         "funding_clamp_big": "4.0000",
        #         "last_trade_price": "3013.13",
        #         "current_funding_rate": "0.0012",
        #         "funding_rate": "0.0012",
        #         "funding_timestamp": 1763532000004,
        #         "daily_base_token_volume": 643235.2763,
        #         "daily_quote_token_volume": 1983505435.673896,
        #         "daily_price_low": 2977.42,
        #         "daily_price_high": 3170.81,
        #         "daily_price_change": -0.3061987051035322
        #     }
        #
        marketId = self.safe_string(ticker, 'market_id')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        last = self.safe_string(ticker, 'last_trade_price')
        high = self.safe_string(ticker, 'daily_price_high')
        low = self.safe_string(ticker, 'daily_price_low')
        baseVolume = self.safe_string(ticker, 'daily_base_token_volume')
        quoteVolume = self.safe_string(ticker, 'daily_quote_token_volume')
        change = self.safe_string(ticker, 'daily_price_change')
        openInterest = self.safe_string(ticker, 'open_interest')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': None,
            'datetime': None,
            'high': high,
            'low': low,
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': None,
            'open': None,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': change,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'markPrice': self.safe_string(ticker, 'mark_price'),
            'indexPrice': self.safe_string(ticker, 'index_price'),
            'openInterest': openInterest,
            'info': ticker,
        }, market)

    async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market

        https://apidocs.lighter.xyz/reference/orderbookdetails

        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchTicker() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'market_id': market['id'],
        }
        response = await self.publicGetOrderBookDetails(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "order_book_details": [
        #             {
        #                 "symbol": "ETH",
        #                 "market_id": 0,
        #                 "status": "active",
        #                 "taker_fee": "0.0000",
        #                 "maker_fee": "0.0000",
        #                 "liquidation_fee": "1.0000",
        #                 "min_base_amount": "0.0050",
        #                 "min_quote_amount": "10.000000",
        #                 "order_quote_limit": "",
        #                 "supported_size_decimals": 4,
        #                 "supported_price_decimals": 2,
        #                 "supported_quote_decimals": 6,
        #                 "size_decimals": 4,
        #                 "price_decimals": 2,
        #                 "quote_multiplier": 1,
        #                 "default_initial_margin_fraction": 500,
        #                 "min_initial_margin_fraction": 200,
        #                 "maintenance_margin_fraction": 120,
        #                 "closeout_margin_fraction": 80,
        #                 "last_trade_price": 3550.69,
        #                 "daily_trades_count": 1197349,
        #                 "daily_base_token_volume": 481297.3509,
        #                 "daily_quote_token_volume": 1671431095.263844,
        #                 "daily_price_low": 3402.41,
        #                 "daily_price_high": 3571.45,
        #                 "daily_price_change": 0.5294300840859545,
        #                 "open_interest": 39559.3278,
        #                 "daily_chart": {},
        #                 "market_config": {
        #                     "market_margin_mode": 0,
        #                     "insurance_fund_account_index": 281474976710655,
        #                     "liquidation_mode": 0,
        #                     "force_reduce_only": False,
        #                     "trading_hours": ""
        #                 }
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'order_book_details', [])
        first = self.safe_dict(data, 0, {})
        return self.parse_ticker(first, market)

    async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market

        https://apidocs.lighter.xyz/reference/orderbookdetails

        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        response = await self.publicGetOrderBookDetails(params)
        tickers = self.safe_list(response, 'order_book_details', [])
        return self.parse_tickers(tickers, symbols)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        # {
        #     "t": 1767700500000,
        #     "o": 3236.86,
        #     "h": 3237.78,
        #     "l": 3235.36,
        #     "c": 3235.39,
        #     "v": 55.1632,
        #     "V": 178530.793575,
        #     "i": 779870452,
        #     "C": "string",
        #     "H": "string",
        #     "L": "string",
        #     "O": "string"
        # }
        #
        return [
            self.safe_integer(ohlcv, 't'),
            self.safe_number(ohlcv, 'o'),
            self.safe_number(ohlcv, 'h'),
            self.safe_number(ohlcv, 'l'),
            self.safe_number(ohlcv, 'c'),
            self.safe_number(ohlcv, 'v'),
        ]

    async def fetch_ohlcv(self, symbol: str, timeframe: str = '1h', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market

        https://apidocs.lighter.xyz/reference/candles

        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest candle to fetch
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOHLCV() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        now = self.milliseconds()
        startTs = None
        endTs = None
        if since is not None:
            startTs = since
            if until is not None:
                endTs = until
            elif limit is not None:
                duration = self.parse_timeframe(timeframe)
                endTs = self.sum(since, duration * limit * 1000)
            else:
                endTs = now
        else:
            endTs = until if (until is not None) else now
            defaultLimit = 100
            if limit is not None:
                startTs = endTs - self.parse_timeframe(timeframe) * 1000 * limit
            else:
                startTs = endTs - self.parse_timeframe(timeframe) * 1000 * defaultLimit
        request: dict = {
            'market_id': market['id'],
            'count_back': 0,
            'resolution': self.safe_string(self.timeframes, timeframe, timeframe),
            'start_timestamp': startTs,
            'end_timestamp': endTs,
        }
        response = await self.publicGetCandles(self.extend(request, params))
        #
        # {
        #     "code": 200,
        #     "r": "1m",
        #     "c": [
        #         {
        #             "t": 1767700500000,
        #             "o": 3236.86,
        #             "h": 3237.78,
        #             "l": 3235.36,
        #             "c": 3235.39,
        #             "v": 55.1632,
        #             "V": 178530.793575,
        #             "i": 779870452,
        #             "C": "string",
        #             "H": "string",
        #             "L": "string",
        #             "O": "string"
        #         }
        #     ]
        # }
        #
        ohlcvs = self.safe_list(response, 'c', [])
        return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)

    def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
        #
        #     {
        #         "market_id": 0,
        #         "exchange": "lighter",
        #         "symbol": "ETH",
        #         "rate": 0.00009599999999999999
        #     }
        #
        marketId = self.safe_string(contract, 'market_id')
        return {
            'info': contract,
            'symbol': self.safe_symbol(marketId, market),
            'markPrice': None,
            'indexPrice': None,
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': None,
            'datetime': None,
            'fundingRate': self.safe_number(contract, 'rate'),
            'fundingTimestamp': None,
            'fundingDatetime': None,
            'nextFundingRate': None,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
            'interval': None,
        }

    async def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
        """
        fetch the current funding rate for multiple symbols

        https://apidocs.lighter.xyz/reference/funding-rates

        :param str[] [symbols]: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-structure>`
        """
        await self.load_markets()
        response = await self.publicGetFundingRates(self.extend(params))
        #
        #     {
        #         "code": 200,
        #         "funding_rates": [
        #             {
        #                 "market_id": 0,
        #                 "exchange": "lighter",
        #                 "symbol": "ETH",
        #                 "rate": 0.00009599999999999999
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'funding_rates', [])
        result = []
        for i in range(0, len(data)):
            exchange = self.safe_string(data[i], 'exchange')
            if exchange == 'lighter':
                result.append(data[i])
        return self.parse_funding_rates(result, symbols)

    async def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders

        https://apidocs.lighter.xyz/reference/account-1

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.by]: fetch balance by 'index' or 'l1_address', defaults to 'index'
        :param str [params.value]: fetch balance value, account index or l1 address
        :returns dict: a `balance structure <https://docs.ccxt.com/?id=balance-structure>`
        """
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchBalance', 'accountIndex', 'account_index')
        defaultType = self.safe_string_2(self.options, 'fetchBalance', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        request: dict = {
            'by': self.safe_string(params, 'by', 'index'),
            'value': accountIndex,
        }
        response = await self.publicGetAccount(self.extend(request, params))
        #
        #     {
        #         "code": "200",
        #         "total": "1",
        #         "accounts": [
        #             {
        #                 "code": "0",
        #                 "account_type": "0",
        #                 "index": "1077",
        #                 "l1_address": "0x15f43D1f2DeE81424aFd891943262aa90F22cc2A",
        #                 "cancel_all_time": "0",
        #                 "total_order_count": "1",
        #                 "total_isolated_order_count": "0",
        #                 "pending_order_count": "0",
        #                 "available_balance": "7996.489834",
        #                 "status": "1",
        #                 "collateral": "9000.000000",
        #                 "account_index": "1077",
        #                 "name": "",
        #                 "description": "",
        #                 "can_invite": True,
        #                 "referral_points_percentage": "",
        #                 "positions": [],
        #                 "assets": [
        #                     {
        #                         "symbol": "ETH",
        #                         "asset_id": "1",
        #                         "balance": "3.00000000",
        #                         "locked_balance": "0.00000000"
        #                     },
        #                     {
        #                         "symbol": "USDC",
        #                         "asset_id": "3",
        #                         "balance": "1000.000000",
        #                         "locked_balance": "0.000000"
        #                     }
        #                 ],
        #                 "total_asset_value": "9536.789088",
        #                 "cross_asset_value": "9536.789088",
        #                 "shares": []
        #             }
        #         ]
        #     }
        #
        result: dict = {'info': response}
        accounts = self.safe_list(response, 'accounts', [])
        for i in range(0, len(accounts)):
            account = accounts[i]
            if type == 'spot':
                assets = self.safe_list(account, 'assets', [])
                for j in range(0, len(assets)):
                    asset = assets[j]
                    codeId = self.safe_string(asset, 'symbol')
                    code = self.safe_currency_code(codeId)
                    balance = self.safe_dict(result, code, self.account())
                    balance['total'] = Precise.string_add(balance['total'], self.safe_string(asset, 'balance'))
                    balance['used'] = Precise.string_add(balance['used'], self.safe_string(asset, 'locked_balance'))
                    result[code] = balance
            else:
                perpUSDC = self.safe_string(account, 'collateral')
                perpBalance = self.safe_dict(result, 'USDC(PERP)', self.account())
                perpBalance['total'] = Precise.string_add(perpBalance['total'], perpUSDC)
                result['USDC'] = perpBalance
        return self.safe_balance(result)

    async def fetch_position(self, symbol: str, params={}):
        """
        fetch data on an open position

        https://apidocs.lighter.xyz/reference/account-1

        :param str symbol: unified market symbol of the market the position is held in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.by]: fetch balance by 'index' or 'l1_address', defaults to 'index'
        :param str [params.value]: fetch balance value, account index or l1 address
        :returns dict: a `position structure <https://docs.ccxt.com/?id=position-structure>`
        """
        positions = await self.fetch_positions([symbol], params)
        return self.safe_dict(positions, 0, {})

    async def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
        """
        fetch all open positions

        https://apidocs.lighter.xyz/reference/account-1

        :param str[] [symbols]: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.by]: fetch balance by 'index' or 'l1_address', defaults to 'index'
        :param str [params.value]: fetch balance value, account index or l1 address
        :returns dict[]: a list of `position structure <https://docs.ccxt.com/?id=position-structure>`
        """
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchPositions', 'accountIndex', 'account_index')
        request: dict = {
            'by': self.safe_string(params, 'by', 'index'),
            'value': accountIndex,
        }
        response = await self.publicGetAccount(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "total": 2,
        #         "accounts": [
        #             {
        #                 "code": 0,
        #                 "account_type": 0,
        #                 "index": 1077,
        #                 "l1_address": "0x15f43D1f2DeE81424aFd891943262aa90F22cc2A",
        #                 "cancel_all_time": 0,
        #                 "total_order_count": 0,
        #                 "total_isolated_order_count": 0,
        #                 "pending_order_count": 0,
        #                 "available_balance": "12582.743947",
        #                 "status": 1,
        #                 "collateral": "9100.242706",
        #                 "account_index": 1077,
        #                 "name": "",
        #                 "description": "",
        #                 "can_invite": True,
        #                 "referral_points_percentage": "",
        #                 "positions": [
        #                     {
        #                         "market_id": 0,
        #                         "symbol": "ETH",
        #                         "initial_margin_fraction": "5.00",
        #                         "open_order_count": 0,
        #                         "pending_order_count": 0,
        #                         "position_tied_order_count": 0,
        #                         "sign": 1,
        #                         "position": "18.0193",
        #                         "avg_entry_price": "2669.84",
        #                         "position_value": "54306.566340",
        #                         "unrealized_pnl": "6197.829558",
        #                         "realized_pnl": "0.000000",
        #                         "liquidation_price": "2191.1107231380406",
        #                         "margin_mode": 0,
        #                         "allocated_margin": "0.000000"
        #                     }
        #                 ],
        #                 "assets": [],
        #                 "total_asset_value": "15298.072264000002",
        #                 "cross_asset_value": "15298.072264000002",
        #                 "shares": []
        #             }
        #         ]
        #     }
        #
        allPositions = []
        accounts = self.safe_list(response, 'accounts', [])
        for i in range(0, len(accounts)):
            account = accounts[i]
            positions = self.safe_list(account, 'positions', [])
            for j in range(0, len(positions)):
                allPositions.append(positions[j])
        return self.parse_positions(allPositions, symbols)

    def parse_position(self, position: dict, market: Market = None):
        #
        #     {
        #         "market_id": 0,
        #         "symbol": "ETH",
        #         "initial_margin_fraction": "5.00",
        #         "open_order_count": 0,
        #         "pending_order_count": 0,
        #         "position_tied_order_count": 0,
        #         "sign": 1,
        #         "position": "18.0193",
        #         "avg_entry_price": "2669.84",
        #         "position_value": "54306.566340",
        #         "unrealized_pnl": "6197.829558",
        #         "realized_pnl": "0.000000",
        #         "liquidation_price": "2191.1107231380406",
        #         "margin_mode": 0,
        #         "allocated_margin": "0.000000"
        #     }
        #
        marketId = self.safe_string(position, 'market_id')
        market = self.safe_market(marketId, market)
        sign = self.safe_integer(position, 'sign')
        side = None
        if sign is not None:
            side = 'long' if (sign == 1) else 'short'
        marginModeId = self.safe_integer(position, 'margin_mode')
        marginMode = None
        if marginModeId is not None:
            marginMode = 'cross' if (marginModeId == 0) else 'isolated'
        imfStr = self.safe_string(position, 'initial_margin_fraction')
        leverage = None
        if imfStr is not None:
            imf = self.parse_to_int(imfStr)
            if imf > 0:
                leverage = 100 / imf
        return self.safe_position({
            'info': position,
            'id': None,
            'symbol': market['symbol'],
            'timestamp': None,
            'datetime': None,
            'isolated': (marginMode == 'isolated'),
            'hedged': None,
            'side': side,
            'contracts': self.safe_number(position, 'position'),
            'contractSize': None,
            'entryPrice': self.safe_number(position, 'avg_entry_price'),
            'markPrice': None,
            'notional': self.safe_number(position, 'position_value'),
            'leverage': leverage,
            'collateral': self.safe_number(position, 'allocated_margin'),
            'initialMargin': None,
            'maintenanceMargin': None,
            'initialMarginPercentage': None,
            'maintenanceMarginPercentage': None,
            'unrealizedPnl': self.safe_number(position, 'unrealized_pnl'),
            'liquidationPrice': self.safe_number(position, 'liquidation_price'),
            'marginMode': marginMode,
            'percentage': None,
        })

    async def fetch_accounts(self, params={}) -> List[Account]:
        """
        fetch all the accounts associated with a profile

        https://apidocs.lighter.xyz/reference/account-1

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.by]: fetch balance by 'index' or 'l1_address', defaults to 'index'
        :param str [params.value]: fetch balance value, account index or l1 address
        :returns dict: a dictionary of `account structures <https://docs.ccxt.com/?id=accounts-structure>` indexed by the account type
        """
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchAccounts', 'accountIndex', 'account_index')
        request: dict = {
            'by': self.safe_string(params, 'by', 'index'),
            'value': accountIndex,
        }
        response = await self.publicGetAccount(self.extend(request, params))
        #
        #     {
        #         "code": "200",
        #         "total": "1",
        #         "accounts": [
        #             {
        #                 "code": "0",
        #                 "account_type": "0",
        #                 "index": "1077",
        #                 "l1_address": "0x15f43D1f2DeE81424aFd891943262aa90F22cc2A",
        #                 "cancel_all_time": "0",
        #                 "total_order_count": "1",
        #                 "total_isolated_order_count": "0",
        #                 "pending_order_count": "0",
        #                 "available_balance": "7996.489834",
        #                 "status": "1",
        #                 "collateral": "9000.000000",
        #                 "account_index": "1077",
        #                 "name": "",
        #                 "description": "",
        #                 "can_invite": True,
        #                 "referral_points_percentage": "",
        #                 "positions": [],
        #                 "assets": [],
        #                 "total_asset_value": "9536.789088",
        #                 "cross_asset_value": "9536.789088",
        #                 "shares": []
        #             }
        #         ]
        #     }
        #
        accounts = self.safe_list(response, 'accounts', [])
        return self.parse_accounts(accounts, params)

    def parse_account(self, account):
        #
        #     {
        #         "code": "0",
        #         "account_type": "0",
        #         "index": "1077",
        #         "l1_address": "0x15f43D1f2DeE81424aFd891943262aa90F22cc2A",
        #         "cancel_all_time": "0",
        #         "total_order_count": "1",
        #         "total_isolated_order_count": "0",
        #         "pending_order_count": "0",
        #         "available_balance": "7996.489834",
        #         "status": "1",
        #         "collateral": "9000.000000",
        #         "account_index": "1077",
        #         "name": "",
        #         "description": "",
        #         "can_invite": True,
        #         "referral_points_percentage": "",
        #         "positions": [],
        #         "assets": [],
        #         "total_asset_value": "9536.789088",
        #         "cross_asset_value": "9536.789088",
        #         "shares": []
        #     }
        #
        accountType = self.safe_string(account, 'account_type')
        return {
            'id': self.safe_string(account, 'account_index'),
            'type': 'main' if (accountType == '0') else 'subaccount',
            'code': None,
            'info': account,
        }

    async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders

        https://apidocs.lighter.xyz/reference/accountactiveorders

        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchOpenOrders', 'accountIndex', 'account_index')
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'fetchOpenOrders', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires an apiKeyIndex parameter')
        await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        market = self.market(symbol)
        request: dict = {
            'market_id': market['id'],
            'account_index': accountIndex,
        }
        response = await self.privateGetAccountActiveOrders(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "orders": [
        #             {
        #                 "order_index": 281474977354074,
        #                 "client_order_index": 0,
        #                 "order_id": "281474977354074",
        #                 "client_order_id": "0",
        #                 "market_index": 0,
        #                 "owner_account_index": 1077,
        #                 "initial_base_amount": "36.0386",
        #                 "price": "2221.60",
        #                 "nonce": 643418,
        #                 "remaining_base_amount": "0.0000",
        #                 "is_ask": True,
        #                 "base_size": 0,
        #                 "base_price": 222160,
        #                 "filled_base_amount": "0.0000",
        #                 "filled_quote_amount": "0.000000",
        #                 "side": "",
        #                 "type": "market",
        #                 "time_in_force": "immediate-or-cancel",
        #                 "reduce_only": False,
        #                 "trigger_price": "0.00",
        #                 "order_expiry": 0,
        #                 "status": "canceled-margin-not-allowed",
        #                 "trigger_status": "na",
        #                 "trigger_time": 0,
        #                 "parent_order_index": 0,
        #                 "parent_order_id": "0",
        #                 "to_trigger_order_id_0": "0",
        #                 "to_trigger_order_id_1": "0",
        #                 "to_cancel_order_id_0": "0",
        #                 "block_height": 102202,
        #                 "timestamp": 1766387932,
        #                 "created_at": 1766387932,
        #                 "updated_at": 1766387932
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'orders', [])
        return self.parse_orders(data, market, since, limit)

    async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently closed orders

        https://apidocs.lighter.xyz/reference/accountinactiveorders

        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchClosedOrders', 'accountIndex', 'account_index')
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'fetchClosedOrders', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires an apiKeyIndex parameter')
        await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        market = self.market(symbol)
        request: dict = {
            'market_id': market['id'],
            'account_index': accountIndex,
            'limit': 100,  # required, max 100
        }
        if limit is not None:
            request['limit'] = min(limit, 100)
        response = await self.privateGetAccountInactiveOrders(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "orders": [
        #             {
        #                 "order_index": 281474977354074,
        #                 "client_order_index": 0,
        #                 "order_id": "281474977354074",
        #                 "client_order_id": "0",
        #                 "market_index": 0,
        #                 "owner_account_index": 1077,
        #                 "initial_base_amount": "36.0386",
        #                 "price": "2221.60",
        #                 "nonce": 643418,
        #                 "remaining_base_amount": "0.0000",
        #                 "is_ask": True,
        #                 "base_size": 0,
        #                 "base_price": 222160,
        #                 "filled_base_amount": "0.0000",
        #                 "filled_quote_amount": "0.000000",
        #                 "side": "",
        #                 "type": "market",
        #                 "time_in_force": "immediate-or-cancel",
        #                 "reduce_only": False,
        #                 "trigger_price": "0.00",
        #                 "order_expiry": 0,
        #                 "status": "canceled-margin-not-allowed",
        #                 "trigger_status": "na",
        #                 "trigger_time": 0,
        #                 "parent_order_index": 0,
        #                 "parent_order_id": "0",
        #                 "to_trigger_order_id_0": "0",
        #                 "to_trigger_order_id_1": "0",
        #                 "to_cancel_order_id_0": "0",
        #                 "block_height": 102202,
        #                 "timestamp": 1766387932,
        #                 "created_at": 1766387932,
        #                 "updated_at": 1766387932
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'orders', [])
        return self.parse_orders(data, market, since, limit)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        #     {
        #         "order_index": 281474977354074,
        #         "client_order_index": 0,
        #         "order_id": "281474977354074",
        #         "client_order_id": "0",
        #         "market_index": 0,
        #         "owner_account_index": 1077,
        #         "initial_base_amount": "36.0386",
        #         "price": "2221.60",
        #         "nonce": 643418,
        #         "remaining_base_amount": "0.0000",
        #         "is_ask": True,
        #         "base_size": 0,
        #         "base_price": 222160,
        #         "filled_base_amount": "0.0000",
        #         "filled_quote_amount": "0.000000",
        #         "side": "",
        #         "type": "market",
        #         "time_in_force": "immediate-or-cancel",
        #         "reduce_only": False,
        #         "trigger_price": "0.00",
        #         "order_expiry": 0,
        #         "status": "canceled-margin-not-allowed",
        #         "trigger_status": "na",
        #         "trigger_time": 0,
        #         "parent_order_index": 0,
        #         "parent_order_id": "0",
        #         "to_trigger_order_id_0": "0",
        #         "to_trigger_order_id_1": "0",
        #         "to_cancel_order_id_0": "0",
        #         "block_height": 102202,
        #         "timestamp": 1766387932,
        #         "created_at": 1766387932,
        #         "updated_at": 1766387932
        #     }
        #
        marketId = self.safe_string(order, 'market_index')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_timestamp(order, 'timestamp')
        isAsk = self.safe_bool(order, 'is_ask')
        side = None
        if isAsk is not None:
            side = 'sell' if isAsk else 'buy'
        type = self.safe_string(order, 'type')
        triggerPrice = self.parse_number(self.omit_zero(self.safe_string(order, 'trigger_price')))
        stopLossPrice = None
        takeProfitPrice = None
        if type is not None:
            if type.find('stop-loss') >= 0:
                stopLossPrice = triggerPrice
            if type.find('take-profit') >= 0:
                takeProfitPrice = triggerPrice
        tif = self.safe_string(order, 'time_in_force')
        status = self.safe_string(order, 'status')
        return self.safe_order({
            'info': order,
            'id': self.safe_string(order, 'order_id'),
            'clientOrderId': self.omit_zero(self.safe_string_2(order, 'client_order_id', 'client_order_index')),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'lastUpdateTimestamp': self.safe_timestamp(order, 'updated_at'),
            'symbol': market['symbol'],
            'type': self.parse_order_type(type),
            'timeInForce': self.parse_order_time_in_foreces(tif),
            'postOnly': None,
            'reduceOnly': self.safe_bool(order, 'reduce_only'),
            'side': side,
            'price': self.safe_string(order, 'price'),
            'triggerPrice': triggerPrice,
            'stopLossPrice': stopLossPrice,
            'takeProfitPrice': takeProfitPrice,
            'amount': self.safe_string(order, 'initial_base_amount'),
            'cost': self.safe_string(order, 'filled_quote_amount'),
            'average': None,
            'filled': self.safe_string(order, 'filled_base_amount'),
            'remaining': self.safe_string(order, 'remaining_base_amount'),
            'status': self.parse_order_status(status),
            'fee': None,
            'trades': None,
        }, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'in-progress': 'open',
            'pending': 'open',
            'open': 'open',
            'filled': 'closed',
            'canceled': 'canceled',
            'canceled-post-only': 'canceled',
            'canceled-reduce-only': 'canceled',
            'canceled-position-not-allowed': 'rejected',
            'canceled-margin-not-allowed': 'rejected',
            'canceled-too-much-slippage': 'canceled',
            'canceled-not-enough-liquidity': 'canceled',
            'canceled-self-trade': 'canceled',
            'canceled-expired': 'expired',
            'canceled-oco': 'canceled',
            'canceled-child': 'canceled',
            'canceled-liquidation': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_type(self, status):
        statuses: dict = {
            'limit': 'limit',
            'market': 'market',
            'stop-loss': 'market',
            'stop-loss-limit': 'limit',
            'take-profit': 'market',
            'take-profit-limit': 'limit',
            'twap': 'twap',
            'twap-sub': 'twap',
            'liquidation': 'market',
        }
        return self.safe_string(statuses, status, status)

    def parse_order_time_in_foreces(self, tif):
        timeInForces: dict = {
            'good-till-time': 'GTC',
            'immediate-or-cancel': 'IOC',
            'post-only': 'PO',
            'Unknown': None,
        }
        return self.safe_string(timeInForces, tif, tif)

    async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from(spot, perp)
        :param str toAccount: account to transfer to(spot, perp)
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param str [params.toAccountIndex]: to account index, defaults to fromAccountIndex
        :param str [params.apiKeyIndex]: api key index
        :param str [params.memo]: hex encoding memo
        :returns dict: a `transfer structure <https://docs.ccxt.com/?id=transfer-structure>`
        """
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'transfer', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' transfer() requires an apiKeyIndex parameter')
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'transfer', 'accountIndex', 'account_index')
        toAccountIndex = None
        toAccountIndex, params = self.handle_option_and_params_2(params, 'transfer', 'toAccountIndex', 'to_account_index', accountIndex)
        currency = self.currency(code)
        if currency['code'] == 'USDC':
            amount = self.parse_to_int(Precise.string_mul(self.pow('10', '6'), self.currency_to_precision(code, amount)))
        elif currency['code'] == 'ETH':
            amount = self.parse_to_int(Precise.string_mul(self.pow('10', '8'), self.currency_to_precision(code, amount)))
        else:
            raise ExchangeError(self.id + ' transfer() only supports USDC and ETH transfers')
        fromRouteType = 0 if (fromAccount == 'perp') else 1  # 0: perp, 1: spot
        toRouteType = 0 if (toAccount == 'perp') else 1
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex)
        memo = self.safe_string(params, 'memo', '0x000000000000000000000000000000')
        params = self.omit(params, ['memo'])
        signRaw: dict = {
            'to_account_index': toAccountIndex,
            'asset_index': self.parse_to_int(currency['id']),
            'from_route_type': fromRouteType,
            'to_route_type': toRouteType,
            'amount': amount,
            'usdc_fee': 0,
            'memo': memo,
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_transfer(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        response = await self.publicPostSendTx(request)
        return self.parse_transfer(response)

    async def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[TransferEntry]:
        """
        fetch a history of internal transfers made on an account

        https://apidocs.lighter.xyz/reference/transfer_history

        :param str code: unified currency code of the currency transferred
        :param int [since]: the earliest time in ms to fetch transfers for
        :param int [limit]: the maximum number of  transfers structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns dict[]: a list of `transfer structures <https://docs.ccxt.com/?id=transfer-structure>`
        """
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchTransfers', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_cursor('fetchTransfers', code, since, limit, params, 'cursor', 'cursor', None, 50)
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchTransfers', 'accountIndex', 'account_index')
        request: dict = {
            'account_index': accountIndex,
        }
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'fetchTransfers', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' fetchTransfers() requires an apiKeyIndex parameter')
        await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        currency = None
        if code is not None:
            currency = self.currency(code)
        response = await self.privateGetTransferHistory(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "transfers": [
        #             {
        #                 "id": "3085014",
        #                 "asset_id": 3,
        #                 "amount": "11.000000",
        #                 "fee": "0.000000",
        #                 "timestamp": 1766387292752,
        #                 "type": "L2TransferOutflow",
        #                 "from_l1_address": "0x15f43D1f2DeE81424aFd891943262aa90F22cc2A",
        #                 "to_l1_address": "0x15f43D1f2DeE81424aFd891943262aa90F22cc2A",
        #                 "from_account_index": 1077,
        #                 "to_account_index": 281474976710608,
        #                 "from_route": "spot",
        #                 "to_route": "spot",
        #                 "tx_hash": "d8e96178273d0938f9ede556edffc0aab8def9ec70c46a65791905291a2f5792af18625406102c80"
        #             }
        #         ],
        #         "cursor": "eyJpbmRleCI6MzA4NDkxNX0="
        #     }
        #
        rows = self.safe_list(response, 'transfers', [])
        cursor = self.safe_string(response, 'cursor')
        first = self.safe_dict(rows, 0)
        if (first is not None) and (cursor is not None):
            rows[0]['cursor'] = cursor
        return self.parse_transfers(rows, currency, since, limit, params)

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        #     {
        #         "id": "3085014",
        #         "asset_id": 3,
        #         "amount": "11.000000",
        #         "fee": "0.000000",
        #         "timestamp": 1766387292752,
        #         "type": "L2TransferOutflow",
        #         "from_l1_address": "0x15f43D1f2DeE81424aFd891943262aa90F22cc2A",
        #         "to_l1_address": "0x15f43D1f2DeE81424aFd891943262aa90F22cc2A",
        #         "from_account_index": 1077,
        #         "to_account_index": 281474976710608,
        #         "from_route": "spot",
        #         "to_route": "spot",
        #         "tx_hash": "d8e96178273d0938f9ede556edffc0aab8def9ec70c46a65791905291a2f5792af18625406102c80"
        #     }
        #
        currencyId = self.safe_string(transfer, 'asset_id')
        code = self.safe_currency_code(currencyId, currency)
        timestamp = self.safe_integer(transfer, 'timestamp')
        fromAccount = self.safe_dict(transfer, 'from', {})
        toAccount = self.safe_dict(transfer, 'to', {})
        return {
            'id': self.safe_string(transfer, 'id'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': code,
            'amount': self.safe_number(transfer, 'amount'),
            'fromAccount': self.safe_string(fromAccount, 'from_account_index'),
            'toAccount': self.safe_string(toAccount, 'to_account_index'),
            'status': None,
            'info': transfer,
        }

    async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account

        https://apidocs.lighter.xyz/reference/deposit_history

        :param str [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 dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param str [params.address]: l1_address
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/?id=transaction-structure>`
        """
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchDeposits', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_cursor('fetchDeposits', code, since, limit, params, 'cursor', 'cursor', None, 50)
        address = None
        address, params = self.handle_option_and_params_2(params, 'fetchDeposits', 'address', 'l1_address')
        if address is None:
            raise ArgumentsRequired(self.id + ' fetchDeposits() requires an address parameter')
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchDeposits', 'accountIndex', 'account_index')
        request: dict = {
            'account_index': accountIndex,
            'l1_address': address,
        }
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'fetchDeposits', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' fetchDeposits() requires an apiKeyIndex parameter')
        await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        response = await self.privateGetDepositHistory(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "deposits": [
        #             {
        #                 "id": "2901843",
        #                 "asset_id": 5,
        #                 "amount": "100000.0",
        #                 "timestamp": 1766112729741,
        #                 "status": "completed",
        #                 "l1_tx_hash": "0xa24d83d58e1fd72b2a44a12d1ec766fb061fa0b806de2fed940b5d8ecd50744d"
        #             }
        #         ],
        #         "cursor": "eyJpbmRleCI6MjkwMTg0MH0="
        #     }
        #
        data = self.safe_list(response, 'deposits', [])
        cursor = self.safe_string(response, 'cursor')
        first = self.safe_dict(data, 0)
        if (first is not None) and (cursor is not None):
            data[0]['cursor'] = cursor
        return self.parse_transactions(data, currency, since, limit)

    async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account

        https://apidocs.lighter.xyz/reference/withdraw_history

        :param str [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 dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/?id=transaction-structure>`
        """
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchWithdrawals', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_cursor('fetchWithdrawals', code, since, limit, params, 'cursor', 'cursor', None, 50)
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchWithdrawals', 'accountIndex', 'account_index')
        await self.load_markets()
        request: dict = {
            'account_index': accountIndex,
        }
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'fetchWithdrawals', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' fetchWithdrawals() requires an apiKeyIndex parameter')
        await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['coin'] = currency['id']
        response = await self.privateGetWithdrawHistory(self.extend(request, params))
        #
        #     {
        #         "code": "200",
        #         "message": "string",
        #         "withdraws": [
        #             {
        #                 "id": "string",
        #                 "amount": "0.1",
        #                 "timestamp": "1640995200",
        #                 "status": "failed",
        #                 "type": "secure",
        #                 "l1_tx_hash": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
        #             }
        #         ],
        #         "cursor": "string"
        #     }
        #
        data = self.safe_list(response, 'withdraws', [])
        cursor = self.safe_string(response, 'cursor')
        first = self.safe_dict(data, 0)
        if (first is not None) and (cursor is not None):
            data[0]['cursor'] = cursor
        return self.parse_transactions(data, currency, since, limit)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # fetchDeposits
        #     {
        #         "id": "2901843",
        #         "asset_id": 5,
        #         "amount": "100000.0",
        #         "timestamp": 1766112729741,
        #         "status": "completed",
        #         "l1_tx_hash": "0xa24d83d58e1fd72b2a44a12d1ec766fb061fa0b806de2fed940b5d8ecd50744d",
        #     }
        #
        # fetchWithdrawals
        #     {
        #         "id": "string",
        #         "amount": "0.1",
        #         "timestamp": "1640995200",
        #         "status": "failed",
        #         "type": "secure",
        #         "l1_tx_hash": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
        #     }
        #
        type = self.safe_string(transaction, 'type')
        if type is None:
            type = 'deposit'
        else:
            type = 'withdrawal'
        timestamp = self.safe_integer(transaction, 'timestamp')
        status = self.safe_string(transaction, 'status')
        return {
            'info': transaction,
            'id': self.safe_string(transaction, 'id'),
            'txid': self.safe_string(transaction, 'l1_tx_hash'),
            'type': type,
            'currency': self.safe_currency_code(self.safe_string(transaction, 'asset_id'), currency),
            'network': None,
            'amount': self.safe_number(transaction, 'amount'),
            'status': self.parse_transaction_status(status),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'address': None,
            'addressFrom': None,
            'addressTo': None,
            'tag': None,
            'tagFrom': None,
            'tagTo': None,
            'updated': None,
            'comment': None,
            'fee': None,
            'internal': None,
        }

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            'failed': 'failed',
            'pending': 'pending',
            'completed': 'ok',
            'claimable': 'ok',
        }
        return self.safe_string(statuses, status, status)

    async def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
        """
        make a withdrawal
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str [tag]:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param str [params.apiKeyIndex]: api key index
        :param int [params.routeType]: wallet type, 0: perp, 1: spot, default is 0
        :returns dict: a `transaction structure <https://docs.ccxt.com/?id=transaction-structure>`
        """
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'withdraw', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' withdraw() requires an apiKeyIndex parameter')
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'withdraw', 'accountIndex', 'account_index')
        currency = self.currency(code)
        if currency['code'] == 'USDC':
            amount = self.parse_to_int(Precise.string_mul(self.pow('10', '6'), self.currency_to_precision(code, amount)))
        elif currency['code'] == 'ETH':
            amount = self.parse_to_int(Precise.string_mul(self.pow('10', '8'), self.currency_to_precision(code, amount)))
        else:
            raise ExchangeError(self.id + ' withdraw() only supports USDC and ETH transfers')
        routeType = self.safe_integer(params, 'routeType', 0)  # 0: perp, 1: spot
        params = self.omit(params, 'routeType')
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex)
        signRaw: dict = {
            'asset_index': self.parse_to_int(currency['id']),
            'route_type': routeType,
            'amount': amount,
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_withdraw(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        response = await self.publicPostSendTx(request)
        return self.parse_transaction(response)

    async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all trades made by the user

        https://apidocs.lighter.xyz/reference/trades

        :param str [symbol]: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :param int [params.until]: timestamp in ms of the latest trade to fetch
        :returns dict[]: a list of `trade structures <https://docs.ccxt.com/?id=trade-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_cursor('fetchMyTrades', symbol, since, limit, params, 'next_cursor', 'cursor', None, 50)
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'fetchMyTrades', 'accountIndex', 'account_index')
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'fetchMyTrades', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires an apiKeyIndex parameter')
        await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        request: dict = {
            'sort_by': 'timestamp',
            'limit': 100,
            'account_index': accountIndex,
        }
        if limit is not None:
            request['limit'] = min(limit, 100)
        until = None
        until, params = self.handle_option_and_params_2(params, 'fetchMyTrades', 'until', 'from')
        if until is not None:
            request['from'] = until
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['market_id'] = market['id']
        response = await self.privateGetTrades(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "trades": [
        #             {
        #                 "trade_id": 17609,
        #                 "tx_hash": "99ffeaa3899fbaa51043840ddf762fd18c182a33b5125092105bee57af11fab04edf5fd90e969abd",
        #                 "type": "trade",
        #                 "market_id": 0,
        #                 "size": "10.2304",
        #                 "price": "2958.75",
        #                 "usd_amount": "30269.196000",
        #                 "ask_id": 281474977339869,
        #                 "bid_id": 562949952870533,
        #                 "ask_client_id": 0,
        #                 "bid_client_id": 0,
        #                 "ask_account_id": 20,
        #                 "bid_account_id": 1077,
        #                 "is_maker_ask": True,
        #                 "block_height": 102070,
        #                 "timestamp": 1766386112741,
        #                 "taker_position_size_before": "0.0000",
        #                 "taker_entry_quote_before": "0.000000",
        #                 "taker_position_sign_changed": True,
        #                 "maker_position_size_before": "-1856.8547",
        #                 "maker_entry_quote_before": "5491685.069325",
        #                 "maker_initial_margin_fraction_before": 500
        #             }
        #         ]
        #     }
        #
        data = self.safe_list(response, 'trades', [])
        for i in range(0, len(data)):
            data[i]['account_index'] = accountIndex
        nextCursor = self.safe_string(response, 'next_cursor')
        first = self.safe_dict(data, 0)
        if (first is not None) and (nextCursor is not None):
            data[0]['next_cursor'] = nextCursor
        return self.parse_trades(data, market, since, limit, params)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        #     {
        #         "trade_id": 17609,
        #         "tx_hash": "99ffeaa3899fbaa51043840ddf762fd18c182a33b5125092105bee57af11fab04edf5fd90e969abd",
        #         "type": "trade",
        #         "market_id": 0,
        #         "size": "10.2304",
        #         "price": "2958.75",
        #         "usd_amount": "30269.196000",
        #         "ask_id": 281474977339869,
        #         "bid_id": 562949952870533,
        #         "ask_client_id": 0,
        #         "bid_client_id": 0,
        #         "ask_account_id": 20,
        #         "bid_account_id": 1077,
        #         "is_maker_ask": True,
        #         "block_height": 102070,
        #         "timestamp": 1766386112741,
        #         "taker_position_size_before": "0.0000",
        #         "taker_entry_quote_before": "0.000000",
        #         "taker_position_sign_changed": True,
        #         "maker_position_size_before": "-1856.8547",
        #         "maker_entry_quote_before": "5491685.069325",
        #         "maker_initial_margin_fraction_before": 500
        #     }
        #
        marketId = self.safe_string(trade, 'market_id')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_integer(trade, 'timestamp')
        accountIndex = self.safe_string(trade, 'account_index')
        askAccountId = self.safe_string(trade, 'ask_account_id')
        bidAccountId = self.safe_string(trade, 'bid_account_id')
        isMakerAsk = self.safe_bool(trade, 'is_maker_ask')
        side = None
        orderId = None
        if accountIndex is not None:
            if accountIndex == askAccountId:
                side = 'sell'
                orderId = self.safe_string(trade, 'ask_id')
            elif accountIndex == bidAccountId:
                side = 'buy'
                orderId = self.safe_string(trade, 'bid_id')
        takerOrMaker = None
        if side is not None and isMakerAsk is not None:
            isMaker = isMakerAsk if (side == 'sell') else not isMakerAsk
            takerOrMaker = 'maker' if isMaker else 'taker'
        return self.safe_trade({
            'info': trade,
            'id': self.safe_string(trade, 'trade_id'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'order': orderId,
            'type': self.safe_string(trade, 'type'),
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': self.safe_string(trade, 'price'),
            'amount': self.safe_string(trade, 'size'),
            'cost': self.safe_string(trade, 'usd_amount'),
            'fee': None,
        }, market)

    async def set_leverage(self, leverage: int, symbol: Str = None, params={}):
        """
        set the level of leverage for a market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param str [params.apiKeyIndex]: api key index
        :param str [params.marginMode]: margin mode, 'cross' or 'isolated'
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        marginMode = None
        marginMode, params = self.handle_option_and_params_2(params, 'setLeverage', 'marginMode', 'margin_mode')
        if marginMode is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires an marginMode parameter')
        return await self.modify_leverage_and_margin_mode(leverage, marginMode, symbol, params)

    async def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
        """
        set margin mode to 'cross' or 'isolated'
        :param str marginMode: 'cross' or 'isolated'
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param str [params.apiKeyIndex]: api key index
        :param int [params.leverage]: required leverage
        :returns dict: response from the exchange
        """
        if marginMode is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires an marginMode parameter')
        leverage = None
        leverage, params = self.handle_option_and_params(params, 'setMarginMode', 'leverage', 'leverage')
        if leverage is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires an leverage parameter')
        return await self.modify_leverage_and_margin_mode(leverage, marginMode, symbol, params)

    async def modify_leverage_and_margin_mode(self, leverage: int, marginMode: str, symbol: Str = None, params={}):
        if (marginMode != 'cross') and (marginMode != 'isolated'):
            raise BadRequest(self.id + ' modifyLeverageAndMarginMode() requires a marginMode parameter that must be either cross or isolated')
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'modifyLeverageAndMarginMode', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' modifyLeverageAndMarginMode() requires an apiKeyIndex parameter')
        if symbol is None:
            raise ArgumentsRequired(self.id + ' modifyLeverageAndMarginMode() requires a symbol argument')
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'modifyLeverageAndMarginMode', 'accountIndex', 'account_index')
        market = self.market(symbol)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex)
        signRaw: dict = {
            'market_index': self.parse_to_int(market['id']),
            'initial_margin_fraction': self.parse_to_int(10000 / leverage),
            'margin_mode': 0 if (marginMode == 'cross') else 1,  # 0: CROSS, 1: ISOLATED
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_update_leverage(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        return await self.publicPostSendTx(request)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param str [params.apiKeyIndex]: api key index
        :returns dict: an `order structure <https://docs.ccxt.com/?id=order-structure>`
        """
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'cancelOrder', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires an apiKeyIndex parameter')
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        clientOrderId = self.safe_string_2(params, 'client_order_index', 'clientOrderId')
        params = self.omit(params, ['client_order_index', 'clientOrderId'])
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'cancelOrder', 'accountIndex', 'account_index')
        market = self.market(symbol)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex)
        signRaw: dict = {
            'market_index': self.parse_to_int(market['id']),
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        if clientOrderId is not None:
            signRaw['order_index'] = self.parse_to_int(clientOrderId)
        elif id is not None:
            signRaw['order_index'] = self.parse_to_int(id)
        else:
            raise ArgumentsRequired(self.id + ' cancelOrder requires order id or client order id')
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_cancel_order(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        response = await self.publicPostSendTx(request)
        return self.parse_order(response, market)

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders
        :param str [symbol]: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.accountIndex]: account index
        :param str [params.apiKeyIndex]: api key index
        :returns dict[]: a list of `order structures <https://docs.ccxt.com/?id=order-structure>`
        """
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'cancelAllOrders', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires an apiKeyIndex parameter')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'cancelAllOrders', 'accountIndex', 'account_index')
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, params)
        signRaw: dict = {
            'time_in_force': 0,  # 0: IMMEDIATE 1: SCHEDULED 2: ABORT
            'time': 0,  # if time_in_force is not IMMEDIATE, set the timestamp_ms here
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_cancel_all_orders(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        response = await self.publicPostSendTx(request)
        return self.parse_orders([response])

    async def cancel_all_orders_after(self, timeout: Int, params={}):
        """
        dead man's switch, cancel all orders after the given timeout
        :param number timeout: time in milliseconds, 0 represents cancel the timer
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: the api result
        """
        if (timeout < 300000) or (timeout > 1296000000):
            raise BadRequest(self.id + ' timeout should be between 5 minutes and 15 days.')
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'cancelOrder', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrdersAfter() requires an apiKeyIndex parameter')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'cancelAllOrdersAfter', 'accountIndex', 'account_index')
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex)
        signRaw: dict = {
            'time_in_force': 1,  # 0: IMMEDIATE 1: SCHEDULED 2: ABORT
            'time': self.milliseconds() + timeout,  # if time_in_force is not IMMEDIATE, set the timestamp_ms here
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_cancel_all_orders(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        response = await self.publicPostSendTx(request)
        return response

    async def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
        """
        add margin
        :param str symbol: unified market symbol
        :param float amount: amount of margin to add
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `margin structure <https://docs.ccxt.com/?id=add-margin-structure>`
        """
        request: dict = {
            'direction': 1,
        }
        return await self.set_margin(symbol, amount, self.extend(request, params))

    async def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
        """
        remove margin from a position
        :param str symbol: unified market symbol
        :param float amount: the amount of margin to remove
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `margin structure <https://docs.ccxt.com/?id=reduce-margin-structure>`
        """
        request: dict = {
            'direction': 0,
        }
        return await self.set_margin(symbol, amount, self.extend(request, params))

    async def set_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
        """
        Either adds or reduces margin in an isolated position in order to set the margin to a specific value
        :param str symbol: unified market symbol of the market to set margin in
        :param float amount: the amount to set the margin to
        :param dict [params]: parameters specific to the bingx api endpoint
        :param str [params.accountIndex]: account index
        :param str [params.apiKeyIndex]: api key index
        :returns dict: A `margin structure <https://docs.ccxt.com/?id=add-margin-structure>`
        """
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, 'setMargin', 'apiKeyIndex', 'api_key_index')
        if apiKeyIndex is None:
            raise ArgumentsRequired(self.id + ' setMargin() requires an apiKeyIndex parameter')
        direction = self.safe_integer(params, 'direction')  # 1 increase margin 0 decrease margin
        if direction is None:
            raise ArgumentsRequired(self.id + ' setMargin() requires a direction parameter either 1(increase margin) or 0(decrease margin)')
        if not self.in_array(direction, [0, 1]):
            raise ArgumentsRequired(self.id + ' setMargin() requires a direction parameter either 1(increase margin) or 0(decrease margin)')
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setMargin() requires a symbol argument')
        await self.load_markets()
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'setMargin', 'accountIndex', 'account_index')
        market = self.market(symbol)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex)
        signRaw: dict = {
            'market_index': self.parse_to_int(market['id']),
            'usdc_amount': self.parse_to_int(Precise.string_mul(self.pow('10', '6'), self.currency_to_precision('USDC', amount))),
            'direction': direction,
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        signer = await self.load_account(self.options['chainId'], self.privateKey, apiKeyIndex, accountIndex, params)
        txType, txInfo = self.lighter_sign_update_margin(signer, self.extend(signRaw, params))
        request: dict = {
            'tx_type': txType,
            'tx_info': txInfo,
        }
        response = await self.publicPostSendTx(request)
        return self.parse_margin_modification(response, market)

    def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
        timestamp = self.safe_integer(data, 'predicted_execution_time_ms')
        return {
            'info': data,
            'symbol': self.safe_string(market, 'symbol'),
            'type': None,
            'marginMode': None,
            'amount': None,
            'total': None,
            'code': 'USDC',
            'status': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = None
        if api == 'root':
            url = self.implode_hostname(self.urls['api']['public'])
        else:
            url = self.implode_hostname(self.urls['api'][api]) + '/api/' + self.version + '/' + path
        if api == 'private':
            headers = {
                'Authorization': self.create_auth(params),
            }
        if params:
            if method == 'POST':
                headers = {
                    'Content-Type': 'multipart/form-data',
                }
                body = params
            else:
                url += '?' + self.rawencode(params)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if not response:
            return None  # fallback to default error handler
        #
        #     {
        #         "code": "200",
        #         "message": "string"
        #     }
        #
        code = self.safe_string(response, 'code')
        message = self.safe_string(response, 'msg')
        if code is not None and code != '0' and code != '200':
            feedback = self.id + ' ' + body
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback)
            raise ExchangeError(feedback)  # unknown message
        return None
