Lighter
A perpetual-futures DEX on a zk rollup, quoted and settled in USDC. The exchange object connects to Lighter perpetuals: placing, modifying and canceling orders and setting leverage are L2 transactions signed with the API key private key, and open/historical order queries use an auth token generated with the same key. Create an API key on Lighter first and note its API key index and the account index.
Names and Configuration
exchange.GetName(): Futures_Lighter (Futures); aliases: zkLighter.
| Field | Required | Default | Description |
|---|---|---|---|
| API Key Private Key | Yes | — | The API key private key created on Lighter (40-byte hex, 0x prefix optional), used to sign L2 transactions and generate auth tokens. |
| API Key Index | Yes | — | API key index number, range 3-254 (0-2 reserved for system) |
| Account Index | Yes | — | Lighter account index for identifying master or sub-account |
| Simulate | No | Unchecked | Connect to the Lighter testnet when checked. |
Trading Pairs and Contract Codes
Pair examples: BTC_USDC, ETH_USDC
Only USDC-quoted perpetuals are listed; write the trading pair as BTC_USDC, and the full symbol looks like BTC_USDC.swap; other quote currencies are rejected. Pairs are case-insensitive and returned in upper case.
| Contract code | Description |
|---|---|
swap | USDC perpetual contract (the only contract type). |
Lighter lists perpetuals only; contract codes other than swap are rejected.
Exchange-specific IO Commands
| Command | Arguments | Description | Returns |
|---|---|---|---|
cross | bool[, symbol] | Cross (true) / isolated (false) margin, for the current contract when symbol is omitted. (more below the table) | {symbol, leverage, marginMode, initialMarginFraction, txHash, ok}; txHash is the hash of the on-chain transaction. |
expiry | number | Lifetime of subsequent limit orders and conditional orders (milliseconds, must be greater than 0), 29 days by default (the exchange's maximum); an order expires at the time of placing plus 10 minutes plus this duration. Market orders are not affected. | true |
api | method, path[, params[, raw[, sign]]] | Call a Lighter endpoint directly; only GET and POST are supported. (more below the table) | The raw exchange response; an error is raised when the response code is not 200. |
The rest of the longer descriptions in the table:
cross: Lighter sets margin mode and leverage in the same on-chain transaction, which also writes the instrument's current leverage (read from the instrument's setting in the exchange account data; a value set in this run within the last 60 seconds takes precedence; the market's default leverage when neither exists).api: By default the auth token generated with the API key private key is attached (the private key and account index must be filled in); withsignset tofalse(it may also be given directly as the 4th argument) no token is sent, which only works for publicGETendpoints.rawis not a request body but a parameter table (a JSON object ora=1&b=2) that replacesparamsentirely when non-empty; it goes into the query string forGETand a form body forPOST. L2 transactions are not signed for the caller.
For the common commands base and currency see exchange.IO.
Common Usage
Set the lifetime of limit orders
javascript
function main() {
// Later limit and conditional orders expire 10 minutes plus 1 hour after they are placed
// (29 days by default); market orders are not affected
exchange.IO("expiry", 60 * 60 * 1000)
var id = exchange.CreateOrder("ETH_USDC.swap", "buy", 2000, 0.01)
Log("Order ID:", id)
}
Call public endpoints and account endpoints that need the auth token with api
javascript
function main() {
// Public endpoints: false as the 4th argument sends no auth token
var books = exchange.IO("api", "GET", "/api/v1/orderBooks", {}, false)
Log("Markets:", books.order_books.length)
var d = exchange.IO("api", "GET", "/api/v1/orderBookDetails", {market_id: 0}, false)
Log(d.order_book_details[0].symbol, "last:", d.order_book_details[0].last_trade_price)
// Account endpoints carry the auth token by default; the account index is in the Info
// of GetAccount()
var idx = exchange.GetAccount().Info.account_index
var params = {account_index: idx, market_id: 0}
var r = exchange.IO("api", "GET", "/api/v1/accountActiveOrders", params)
Log("Open orders on market_id 0:", r.orders.length)
}
Function Support
No unsupported standard functions.
Served through a fallback:
exchange.GetRecords: Native periods are 1m, 5m, 15m, 30m, 1h, 4h, 12h, 1d, 1w; other periods are synthesized by the docker.
Margin, Account and Simulation
Margin mode: Switch cross/isolated margin per contract with exchange.IO("cross", bool) and set leverage with exchange.SetMarginLevel(). On Lighter both are the same on-chain transaction (UpdateLeverage); every call sends one and always writes both leverage and margin mode, keeping the other one at the instrument's current value: exchange.SetMarginLevel() keeps the current margin mode and exchange.IO("cross", ...) keeps the current leverage. The current values are read from the instrument's setting in the exchange account data (present even without a position); a value set in this run within the last 60 seconds takes precedence (the exchange data lags); without either, the market's default leverage and cross. exchange.SetMarginLevel() also accepts {marginMode: "isolated"} (or cross) in the extra parameters to set the margin mode at the same time. A position's MarginLevel is derived from the initial margin fraction in the exchange's position data.
Position mode: One-way (net) positions only; hedge mode is not supported.
Account: The account index selects the master account or a sub-account; the account and positions are queried by account index, and USDC is the only asset.
Simulation: The Simulate box connects to the Lighter testnet (https://testnet.zklighter.elliot.ai). The API address can also be switched with exchange.SetBase() (or exchange.IO("base", url)): transactions are signed for the testnet when the address contains testnet, otherwise for the mainnet.
Notes
| Function | Behavior on Futures_Lighter |
|---|---|
| exchange.GetTicker() | Bid and ask come from the order book; the exchange's market data has no timestamp, so Time is the local time |
| exchange.GetTickers() | Buy and Sell are each instrument's last trade price (the exchange has no batch order book endpoint); use exchange.GetTicker() or exchange.GetDepth() when you need the best bid and ask. Placeholder instruments whose last price, 24h high/low and volume are all zero are omitted |
| exchange.GetDepth() | The exchange returns individual resting orders; each order is its own level and orders at the same price are not merged, so a price may appear more than once |
| exchange.GetFundings() | Rate is the exchange's rate divided by 8 (an hourly rate), Interval is 1 hour and Time is the next full hour |
| exchange.CreateOrder() | Limit orders support GTC (resting for the exchange.IO("expiry", ...) lifetime), IOC and post-only; FOK is not supported. A market order is an IOC order whose worst fill price is the best opposite price at the time of placing plus or minus a slippage (a buy no higher than the best ask × (1 + slippage), a sell no lower than the best bid × (1 − slippage)); the slippage defaults to 0.5% and can be set with the order extra parameter slippage (such as 0.01, greater than 0 and less than 1, the same parameter as for market conditional orders); whatever the book cannot fill within that price is canceled. An order that fills nothing is canceled with a DealAmount of 0, and status in Info gives the reason (such as canceled-too-much-slippage) |
| exchange.CreateConditionOrder() | Take-profit and stop-loss only, no OCO. With an order price it is a limit conditional order; with an order price of -1 (market) it executes at market once triggered (IOC) with a worst fill price of the trigger price ±5% (up for buys, down for sells), adjustable with {slippage: 0.1} in the extra parameters |
| exchange.ModifyOrder() | Modifies in place, the order ID stays the same; a price or amount that is not passed keeps its current value |
| exchange.GetOrders(), exchange.GetConditionOrders() | The exchange API requires an instrument: without a symbol, or with an empty string, only the current instrument is queried. GetOrders returns limit and market orders only and GetConditionOrders take-profit/stop-loss orders only (other types such as TWAP appear in neither); range arguments such as USDC.swap are not supported |
| exchange.GetOrder() | Searches only the open orders and the latest 100 historical orders of the instrument; older orders return null |
| exchange.ModifyConditionOrder() | Modifies the amount, trigger price and order price in place; the order ID stays the same. The limit/market variant cannot change: an order price of -1 on a limit conditional order, or a positive one on a market conditional order, fails (cancel and recreate instead); a market conditional order's worst fill price is recomputed from the new trigger price |
| exchange.GetHistoryOrders(), exchange.GetHistoryConditionOrders() | The exchange API requires an instrument: the current one is queried when omitted, and an empty string or a range raises an error; the since parameter is not supported and raises an error; without limit the latest 100 and 500 orders are returned respectively |