# -*- 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 NotSupported
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,
            'quoteJsonNumbers': False,
            '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': {
                    '21146': ExchangeError,  # system account cannot be an integrator
                    '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',
                'builderFee': True,
                'chainId': 304,
                'accountIndex': None,
                'apiKeyIndex': None,
                'lighterPrivateKey': 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
                'integratorAccountIndex': 718718,
                'integratorMakerFee': 1000,
                'integratorTakerFee': 1000,
                'authDeadlineExpiry': 28800,  # 8h validity for auth tokens
                'authDeadlineMinimumRemaining': 60,
            },
            '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: str, accountIndex: str, params={}):
        self.init_auth_object(accountIndex, apiKeyIndex)
        cachedAuths = self.safe_dict(self.options['auths'][accountIndex], apiKeyIndex)
        signer = self.safe_value(cachedAuths, 'signer')
        if signer is not None:
            return signer
        libraryPath = None
        libraryPath, params = self.handle_option_and_params(params, 'loadAccount', 'libraryPath')
        lighterPrivateKeyIsSet = (privateKey is not None) and (privateKey != '')
        if lighterPrivateKeyIsSet and (libraryPath is not None) and (apiKeyIndex is not None) and (accountIndex is not None):
            # load lighter library, and create lighter client
            signer = await self.load_lighter_library(libraryPath, chainId, privateKey, self.parse_to_int(apiKeyIndex), self.parse_to_int(accountIndex), True)
            self.options['auths'][accountIndex][apiKeyIndex]['signer'] = signer
            return signer
        privateKeyIsSet = (self.privateKey is not None) and (self.privateKey != '')
        if privateKeyIsSet and (apiKeyIndex is not None) and (accountIndex is not None):
            if len(self.privateKey) > 66:
                raise NotSupported(self.id + ' after the latest update(v4.5.50), CCXT now expects the l1 private key to be provided in the credentials. Please check for more details: https://github.com/ccxt/ccxt/wiki/FAQ#how-to-use-the-lighter-exchange-in-ccxt')
            # load lighter library without creating lighter client
            signer = await self.load_lighter_library(libraryPath, chainId, '', self.parse_to_int(apiKeyIndex), self.parse_to_int(accountIndex), False)
            self.options['auths'][accountIndex][apiKeyIndex]['signer'] = signer
            res = await self.change_api_key()
            await self.handle_builder_fee_approval(self.parse_to_int(accountIndex), self.parse_to_int(apiKeyIndex))
            return res
        return signer

    def init_auth_object(self, strAccountIndex: str, strApiKeyIndex: str):
        if not ('auths' in self.options):
            self.options['auths'] = {}
        if not (strAccountIndex in self.options['auths']):
            self.options['auths'][strAccountIndex] = {}
        if not (strApiKeyIndex in self.options['auths'][strAccountIndex]):
            self.options['auths'][strAccountIndex][strApiKeyIndex] = {
                'signer': None,
                'lighterPrivateKey': None,
                'deadline': None,
                'token': None,
            }

    def get_lighter_private_key(self, strAccountIndex: str, strApiKeyIndex: str):
        if not ('auths' in self.options):
            return None
        if not (strAccountIndex in self.options['auths']):
            return None
        if not (strApiKeyIndex in self.options['auths'][strAccountIndex]):
            return None
        if not ('lighterPrivateKey' in self.options['auths'][strAccountIndex][strApiKeyIndex]):
            return None
        return self.options['auths'][strAccountIndex][strApiKeyIndex]['lighterPrivateKey']

    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 dict [params]: extra parameters specific to the exchange API endpoint
        :returns boolean: True if the signer was loaded, False otherwise
        """
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_api_key_index(params, 'loadAccount', 'apiKeyIndex', 'api_key_index')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'loadAccount', 'accountIndex', 'account_index')
        if accountIndex is None:
            raise ArgumentsRequired(self.id + ' requires accountIndex or account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        self.init_auth_object(strAccountIndex, strApiKeyIndex)
        signer = self.safe_dict(self.options['auths'][strAccountIndex][strApiKeyIndex], 'signer')
        if signer is not None:
            return True
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex)
        await self.handle_builder_fee_approval(accountIndex, apiKeyIndex)
        return(signer is not None)

    def handle_api_key_index(self, params: object, methodName1: str, optionName1: str, optionName2: str, defaultValue=None) -> List[Any]:
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_option_and_params_2(params, methodName1, optionName1, optionName2, defaultValue)
        if (apiKeyIndex is None) or (apiKeyIndex < 4) or (apiKeyIndex > 254):
            # apiKeyIndex = self.rand_number(2)
            apiKeyIndex = 254
            self.options['apiKeyIndex'] = apiKeyIndex  # default to a value to avoid overriding other keys
        return [self.parse_to_int(apiKeyIndex), params]

    async def handle_account_index(self, params: object, methodName1: str, optionName1: str, optionName2: str, defaultValue=None) -> List[Any]:
        accountIndex = None
        accountIndex, params = self.handle_option_and_params_2(params, methodName1, optionName1, optionName2, defaultValue)
        if accountIndex is None:
            walletAddress = self.walletAddress
            if self.privateKey is not None:
                if len(self.privateKey) > 66:
                    raise NotSupported(self.id + ' after the latest update(v4.5.50), CCXT now expects the l1 private key to be provided in the credentials. Please check for more details: https://github.com/ccxt/ccxt/wiki/FAQ#how-to-use-the-lighter-exchange-in-ccxt')
                walletAddress = self.eth_get_address_from_private_key(self.privateKey)
            if walletAddress is None or walletAddress == '':
                raise ArgumentsRequired(self.id + ' ' + methodName1 + '() requires an ' + optionName1 + '/' + optionName2 + ' parameter or walletAddress to fetch accountIndex. Alternatively set privateKey in credentials to enable automatic walletAddress detection.')
            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_api_key_index(params, 'createSubAccount', 'apiKeyIndex', 'api_key_index')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'createSubAccount', 'accountIndex', 'account_index')
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, params)
        signRaw: dict = {
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, 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_string_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_string(res, 0)
        accountIndex = self.safe_string_2(params, 'accountIndex', 'account_index')
        if accountIndex is None:
            res = self.handle_option_and_params_2({}, 'createAuth', 'accountIndex', 'account_index')
            accountIndex = self.safe_string(res, 0)
        auths = self.safe_dict(self.options, 'auths')
        accountAuths = self.safe_dict(auths, accountIndex)
        cachedAuth = self.safe_dict(accountAuths, apiKeyIndex)
        cachedDeadline = self.safe_integer(cachedAuth, 'deadline')
        if cachedDeadline is not None:
            minimumDeadline = self.seconds() + self.safe_integer(self.options, 'authDeadlineMinimumRemaining')
            if cachedDeadline >= minimumDeadline:
                return self.safe_string(cachedAuth, 'token')
        deadline = self.seconds() + self.safe_integer(self.options, 'authDeadlineExpiry')
        request = {
            'deadline': deadline,
            'api_key_index': self.parse_to_int(apiKeyIndex),
            'account_index': self.parse_to_int(accountIndex),
        }
        token = self.lighter_create_auth_token(self.options['auths'][accountIndex][apiKeyIndex]['signer'], request)
        self.options['auths'][accountIndex][apiKeyIndex]['deadline'] = deadline
        self.options['auths'][accountIndex][apiKeyIndex]['token'] = token
        return token

    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 hash_message(self, message: str):
        binaryMessage = self.encode(message)
        binaryMessageLength = self.binary_length(binaryMessage)
        x19 = self.base16_to_binary('19')
        newline = self.base16_to_binary('0a')
        prefix = self.binary_concat(x19, self.encode('Ethereum Signed Message:'), newline, self.encode(self.number_to_string(binaryMessageLength)))
        return '0x' + self.hash(self.binary_concat(prefix, binaryMessage), 'keccak', 'hex')

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

    def sign_l1_and_prepare_tx_info(self, txInfo, message, privateKey):
        hashMessage = self.hash_message(message)
        signature = self.sign_hash(hashMessage, privateKey)
        decTxInfo = self.parse_json(txInfo)
        decTxInfo['L1Sig'] = signature
        return self.json(decTxInfo)

    async def handle_builder_fee_approval(self, accountIndex: float, apiKeyIndex: float):
        buildFee = self.safe_bool(self.options, 'builderFee', True)
        if not buildFee:
            return False
        approvedBuilderFee = self.safe_bool(self.options, 'approvedBuilderFee', False)
        if approvedBuilderFee:
            return True
        try:
            builder = self.safe_integer(self.options, 'integratorAccountIndex', 718718)
            takerFeeRate = self.safe_integer(self.options, 'integratorTakerFee', 1000)
            makerFeeRate = self.safe_integer(self.options, 'integratorMakerFee', 1000)
            await self.approve_builder_fee(builder, takerFeeRate, makerFeeRate, accountIndex, apiKeyIndex)
            self.options['approvedBuilderFee'] = True
        except Exception as e:
            self.options['builderFee'] = False
        return True

    async def approve_builder_fee(self, builder: float, takerFeeRate: float, makerFeeRate: float, accountIndex: float, apiKeyIndex: float, params: object = {}):
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, self.extend(params, {'skipNonce': False}))
        expiry = self.milliseconds() + 365 * 864000
        signRaw = {
            'integrator_account_index': builder,
            'integrator_taker_fee': takerFeeRate,
            'integrator_maker_fee': makerFeeRate,
            'approval_expiry': expiry,
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        txType, txInfo, messageToSign = self.lighter_sign_approve_integrator(signer, self.extend(signRaw, params))
        newTxInfo = self.sign_l1_and_prepare_tx_info(txInfo, messageToSign, self.privateKey)
        request = {
            'tx_type': txType,
            'tx_info': newTxInfo,
        }
        response = await self.publicPostSendTx(request)
        return response

    async def change_api_key(self, params: object = {}):
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_api_key_index(params, 'changeApiKey', 'apiKeyIndex', 'api_key_index')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'changeApiKey', 'accountIndex', 'account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signerNotLoad = self.options['auths'][strAccountIndex][strApiKeyIndex]['signer']
        privateKey, publicKey = self.lighter_generate_api_key(signerNotLoad)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, self.extend(params, {'skipNonce': False}))
        signRaw = {
            'pubkey': self.encode(publicKey),
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        # create lighter client
        signer = self.lighter_create_client(signerNotLoad, self.options['chainId'], privateKey, apiKeyIndex, accountIndex)
        txType, txInfo, messageToSign = self.lighter_sign_change_pubkey(signer, self.extend(signRaw, params))
        newTxInfo = self.sign_l1_and_prepare_tx_info(txInfo, messageToSign, self.privateKey)
        request = {
            'tx_type': txType,
            'tx_info': newTxInfo,
        }
        await self.publicPostSendTx(request)
        self.options['auths'][strAccountIndex][strApiKeyIndex]['lighterPrivateKey'] = privateKey
        self.options['auths'][strAccountIndex][strApiKeyIndex]['signer'] = signer  # reassign signer in go
        await self.handle_builder_fee_approval(accountIndex, apiKeyIndex)
        return signer

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

    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_api_key_index(params, 'createOrder', 'apiKeyIndex', 'api_key_index')
        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)
        if nonce is not None:
            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))
        if self.safe_bool(self.options, 'builderFee', True):
            request['integrator_account_index'] = self.options['integratorAccountIndex']
            request['integrator_taker_fee'] = self.options['integratorTakerFee']
            request['integrator_maker_fee'] = self.options['integratorMakerFee']
        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
        # avoid skipNonce for l1 operations
        skipNonce = True
        skipNonce, params = self.handle_option_and_params(params, 'fetchNonce', 'skipNonce', True)
        if skipNonce:
            return self.milliseconds()
        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']
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        # the nonce could be updated
        if self.safe_integer(order, 'nonce') is None:
            order['nonce'] = await self.fetch_nonce(accountIndex, apiKeyIndex)
        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,
            }
            if self.safe_bool(self.options, 'builderFee', True):
                signingPayload['integrator_account_index'] = order['integrator_account_index']
                signingPayload['integrator_taker_fee'] = order['integrator_taker_fee']
                signingPayload['integrator_maker_fee'] = order['integrator_maker_fee']
            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>`
        """
        await self.load_markets()
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_api_key_index(params, 'editOrder', 'apiKeyIndex', 'api_key_index')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'editOrder', 'accountIndex', 'account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        market = self.market(symbol)
        marketInfo = self.safe_dict(market, 'info')
        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)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, params)
        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,
            'integrator_account_index': self.options['integratorAccountIndex'],
            'integrator_taker_fee': self.options['integratorTakerFee'],
            'integrator_maker_fee': self.options['integratorMakerFee'],
        }
        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",
        #        "message": "string",
        #        "order_book_details": [
        #            {
        #                "symbol": "ETH",
        #                "market_id": 0,
        #                "market_type": "perp",
        #                "base_asset_id": 0,
        #                "quote_asset_id": 0,
        #                "status": "active",
        #                "taker_fee": "0.0001",
        #                "maker_fee": "0.0000",
        #                "liquidation_fee": "0.01",
        #                "min_base_amount": "0.01",
        #                "min_quote_amount": "0.1",
        #                "supported_size_decimals": "4",
        #                "supported_price_decimals": "4",
        #                "supported_quote_decimals": "4",
        #                "order_quote_limit": "281474976.710655",
        #                "size_decimals": "4",
        #                "price_decimals": "4",
        #                "quote_multiplier": "10000",
        #                "default_initial_margin_fraction": "100",
        #                "min_initial_margin_fraction": "100",
        #                "maintenance_margin_fraction": "50",
        #                "closeout_margin_fraction": "100",
        #                "last_trade_price": "3024.66",
        #                "daily_trades_count": "68",
        #                "daily_base_token_volume": "235.25",
        #                "daily_quote_token_volume": "93566.25",
        #                "daily_price_low": "3014.66",
        #                "daily_price_high": "3024.66",
        #                "daily_price_change": "3.66",
        #                "open_interest": "93.0",
        #                "daily_chart": "{1640995200:3024.66}",
        #                "market_config": {
        #                    "market_margin_mode": 0,
        #                    "insurance_fund_account_index": 281474976710655,
        #                    "liquidation_mode": 0,
        #                    "force_reduce_only": False,
        #                    "funding_fee_discounts_enabled": True,
        #                    "trading_hours": "",
        #                    "hidden": True
        #                },
        #                "strategy_index": 0
        #            }
        #        ],
        #        "spot_order_book_details": [
        #            {
        #                "symbol": "ETH/USDC",
        #                "market_id": 2048,
        #                "market_type": "spot",
        #                "base_asset_id": 1,
        #                "quote_asset_id": 3,
        #                "status": "active",
        #                "taker_fee": "0.0000",
        #                "maker_fee": "0.0000",
        #                "liquidation_fee": "0.0000",
        #                "min_base_amount": "0.0001",
        #                "min_quote_amount": "0.000001",
        #                "order_quote_limit": "2500000.000000",
        #                "supported_size_decimals": 4,
        #                "supported_price_decimals": 2,
        #                "supported_quote_decimals": 6,
        #                "size_decimals": 4,
        #                "price_decimals": 2,
        #                "last_trade_price": 2731.79,
        #                "daily_trades_count": 126993,
        #                "daily_base_token_volume": 1203.0962,
        #                "daily_quote_token_volume": 3516374.947553,
        #                "daily_price_low": 2717.47,
        #                "daily_price_high": 3044.21,
        #                "daily_price_change": -10.2389493724579,
        #                "daily_chart": "{1640995200:3024.66}"
        #            }
        #        ]
        #    }
        #
        spotMarkets = self.safe_list(response, 'spot_order_book_details', [])
        swapMarkets = self.safe_list(response, 'order_book_details', [])
        markets = self.array_concat(spotMarkets, swapMarkets)
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'market_id')
            type = self.safe_string(market, 'market_type')
            type = 'swap' if (type == 'perp') else type
            baseId = self.safe_string(market, 'symbol')
            if baseId is not None and baseId.find('/') != -1:
                baseId = baseId.split('/')[0]
            quoteId = 'USDC'
            settleId = 'USDC' if (type == 'swap') else None
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = self.safe_currency_code(settleId)
            symbol = base + '/' + quote
            if settle is not None:
                symbol = symbol + ':' + settle
            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': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': type,
                'spot': type == 'spot',
                'margin': False,
                'swap': type == 'swap',
                'future': False,
                'option': False,
                'active': self.safe_string(market, 'status') == 'active',
                'contract': type == 'swap',
                'linear': True if (type == 'swap') else None,
                'inverse': False if (type == 'swap') else None,
                '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': self.safe_number(market, 'order_quote_limit'),
                    },
                },
                '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)
        if self.check_required_credentials(False):
            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": 1765419046808
        #             }
        #         ],
        #         "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": 281474976710654,
        #             "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": ""
        #                 }
        #             }
        #         ]
        #     }
        #
        spotTickers = self.safe_list(response, 'spot_order_book_details', [])
        swapTickers = self.safe_list(response, 'order_book_details', [])
        tickers = self.array_concat(spotTickers, swapTickers)
        first = self.safe_dict(tickers, 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)
        spotTickers = self.safe_list(response, 'spot_order_book_details', [])
        swapTickers = self.safe_list(response, 'order_book_details', [])
        tickers = self.array_concat(spotTickers, swapTickers)
        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
        :param str [params.type]: 'spot', 'swap', default is 'swap'
        :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:
                perpBalance = self.safe_dict(result, 'USDC', self.account())
                perpUSDCTotal = self.safe_string(account, 'collateral')
                perpUSDCFree = self.safe_string(account, 'available_balance')
                perpBalance['total'] = Precise.string_add(perpBalance['total'], perpUSDCTotal)
                perpBalance['free'] = Precise.string_add(perpBalance['free'], perpUSDCFree)
                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_api_key_index(params, 'fetchOpenOrders', 'apiKeyIndex', 'api_key_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, 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_api_key_index(params, 'fetchClosedOrders', 'apiKeyIndex', 'api_key_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, 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')
        if isAsk is None:
            isAskAsInteger = self.safe_integer(order, 'is_ask')
            if isAskAsInteger is not None:
                isAsk = isAskAsInteger == 1
        side = None
        if isAsk is not None:
            side = 'sell' if isAsk else 'buy'
        type = self.safe_string(order, 'type')
        if type is None:
            typeAsInteger = self.safe_integer(order, 'order_type')
            type = self.parse_order_type_integer(typeAsInteger)
        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
        # Try to parse to integer first, because parsing an integer to a string wouldn't result in None
        tif = None
        tifAsInteger = self.safe_integer(order, 'time_in_force')
        if tifAsInteger is not None:
            tif = self.parse_order_time_in_force_integer(tifAsInteger)
        else:
            tif = self.safe_string(order, 'time_in_force')
        reduceOnly = self.safe_bool(order, 'reduce_only')
        if reduceOnly is None:
            reduceOnlyAsInteger = self.safe_integer(order, 'reduce_only')
            if reduceOnlyAsInteger is not None:
                reduceOnly = reduceOnlyAsInteger == 1
        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_force(tif),
            'postOnly': tif == 'post-only',
            'reduceOnly': reduceOnly,
            '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, type):
        types: 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(types, type, type)

    def parse_order_type_integer(self, typeInteger):
        if typeInteger is None:
            return None
        types: dict = {
            '0': 'limit',
            '1': 'market',
            '2': 'stop-loss',
            '3': 'stop-loss-limit',
            '4': 'take-profit',
            '5': 'take-profit-limit',
            '6': 'twap',
            '7': 'twap-sub',
            '8': 'liquidation',
        }
        return self.safe_string(types, str(typeInteger))

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

    def parse_order_time_in_force_integer(self, tifInteger):
        timeInForces: dict = {
            '0': 'immediate-or-cancel',
            '1': 'good-till-time',
            '2': 'post-only',
        }
        return self.safe_string(timeInForces, str(tifInteger))

    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>`
        """
        await self.load_markets()
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_api_key_index(params, 'transfer', 'apiKeyIndex', 'api_key_index')
        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)
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        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
        memo = self.safe_string(params, 'memo', '0x000000000000000000000000000000')
        params = self.omit(params, ['memo'])
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, params)
        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,
        }
        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>`
        """
        await self.load_markets()
        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_api_key_index(params, 'fetchTransfers', 'apiKeyIndex', 'api_key_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, 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>`
        """
        await self.load_markets()
        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')
        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_api_key_index(params, 'fetchDeposits', 'apiKeyIndex', 'api_key_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, 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_api_key_index(params, 'fetchWithdrawals', 'apiKeyIndex', 'api_key_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, 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>`
        """
        await self.load_markets()
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_api_key_index(params, 'withdraw', 'apiKeyIndex', 'api_key_index')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'withdraw', 'accountIndex', 'account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        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, params)
        signRaw: dict = {
            'asset_index': self.parse_to_int(currency['id']),
            'route_type': routeType,
            'amount': amount,
            'nonce': nonce,
            'api_key_index': apiKeyIndex,
            'account_index': accountIndex,
        }
        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_api_key_index(params, 'fetchMyTrades', 'apiKeyIndex', 'api_key_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, 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={}):
        await self.load_markets()
        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_api_key_index(params, 'modifyLeverageAndMarginMode', 'apiKeyIndex', 'api_key_index')
        if symbol is None:
            raise ArgumentsRequired(self.id + ' modifyLeverageAndMarginMode() requires a symbol argument')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'modifyLeverageAndMarginMode', 'accountIndex', 'account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        market = self.market(symbol)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, params)
        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,
        }
        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>`
        """
        await self.load_markets()
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_api_key_index(params, 'cancelOrder', 'apiKeyIndex', 'api_key_index')
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        market = self.market(symbol)
        clientOrderId = self.safe_string_2(params, 'client_order_index', 'clientOrderId')
        params = self.omit(params, ['client_order_index', 'clientOrderId'])
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'cancelOrder', 'accountIndex', 'account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, params)
        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')
        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>`
        """
        await self.load_markets()
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_api_key_index(params, 'cancelAllOrders', 'apiKeyIndex', 'api_key_index')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'cancelAllOrders', 'accountIndex', 'account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        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,
        }
        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
        """
        await self.load_markets()
        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_api_key_index(params, 'cancelOrder', 'apiKeyIndex', 'api_key_index')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'cancelAllOrdersAfter', 'accountIndex', 'account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, params)
        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,
        }
        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>`
        """
        await self.load_markets()
        apiKeyIndex = None
        apiKeyIndex, params = self.handle_api_key_index(params, 'setMargin', 'apiKeyIndex', 'api_key_index')
        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')
        accountIndex = None
        accountIndex, params = await self.handle_account_index(params, 'setMargin', 'accountIndex', 'account_index')
        strAccountIndex = self.number_to_string(accountIndex)
        strApiKeyIndex = self.number_to_string(apiKeyIndex)
        signer = await self.load_account(self.options['chainId'], self.get_lighter_private_key(strAccountIndex, strApiKeyIndex), strApiKeyIndex, strAccountIndex, params)
        market = self.market(symbol)
        nonce = await self.fetch_nonce(accountIndex, apiKeyIndex, params)
        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,
        }
        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
