Signing Transactions
Market, limit, stop and TWAP orders; modify, cancel and batch; leverage and margin.
Traders can trade programmatically using the sendTx and sendTxBatch REST endpoints, or json/sendTx and json/sendTxBatch types via Websocket. Creating an API key is required to sign transactions.
Orders that have the correct syntax will be accepted by the API servers, returning code=200. This does not guarantee the execution of your order, as the sequencer could still reject it if parameters are not set properly. To monitor orders execution, see Confirming execution.
Sign Transactions
Every transaction needs to be signed using the SignerClient. Using the Python SDK, you can initialize it as follows:
client = lighter.SignerClient(
url=BASE_URL,
api_private_keys={API_KEY_INDEX:PRIVATE_KEY},
account_index=ACCOUNT_INDEX
)The code for the signer can be found in the same repo, in the signer_client.py file. You may notice that it uses a binary for the signer: the code for it can be found in the lighter-go public repo, and you can compile it yourself using the justfile.
The TypeScript examples on this page use the WASM signer from the TypeScript quickstart and this helper:
async function sendTx(tx: { txType: number; txInfo: string }) {
const r = await fetch(`${BASE_URL}/api/v1/sendTx`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ tx_type: String(tx.txType), tx_info: tx.txInfo }),
}).then(r => r.json());
if (r.code !== 200) throw new Error(`${r.code}: ${r.message}`);
return r; // { tx_hash, predicted_execution_time_ms, volume_quota_remaining }
}Every order carries a nonce and uses integer prices and sizes. Both are explained in Core concepts: see Nonce (including which rejected transactions consume a nonce) and Integer prices and sizes (including minimum order sizes).
Order parameters
| Parameter | Notes |
|---|---|
marketIndex | market_id from orderBookDetails |
clientOrderIndex | Your ID (uint48, ≤ 2^48−1), unique across all markets. Used to cancel/modify. |
baseAmount | Integer: size × 10^size_decimals |
price | Integer: price × 10^price_decimals. For taker orders = worst acceptable price: if the sequencer cannot offer an equal or better price, the order is cancelled |
isAsk | 0 buy, 1 sell |
orderType | See Order types |
timeInForce | 0 Immediate-or-cancel · 1 Good-till-time · 2 Post-only |
reduceOnly | 1 to only reduce a position |
triggerPrice | For stop-loss / take-profit |
orderExpiry | Unix ms timestamp, between 5 minutes and 30 days ahead. 0 for IOC orders |
When cancelling, you may see order_index instead of client_order_index as an argument – you can use the same value. If client_order_index is missing (e.g. if you trade from the front-end, that'll be 0), you can use trade_id (assigned by the exchange) to reference the order.
Order types
| Value | Type | Use |
|---|---|---|
| 0 | Limit | Resting or IOC limit order |
| 1 | Market | Immediate execution up to price |
| 2 | Stop-loss | Market order when triggerPrice is hit |
| 3 | Stop-loss limit | Limit order when triggerPrice is hit |
| 4 | Take-profit | Market order when triggerPrice is hit |
| 5 | Take-profit limit | Limit order when triggerPrice is hit |
| 6 | TWAP | Time-weighted execution |
Additionally, there are two internal order types: TWAPSubOrder (7) and LiquidationOrder (8).
Other order enums
| Enum | Values |
|---|---|
| Grouping type | 1 OTO · 2 OCO · 3 OTOCO |
| Self-trade behavior | 0 Expire maker · 1 Expire taker · 2 Expire both · 3 Reduce |
| Self-trade equality | 0 Same account index · 1 Same master account |
| Cancel-all TIF | 0 Immediate · 1 Scheduled · 2 Abort |
| Margin mode | 0 Cross · 1 Isolated |
| Route type | 0 Perps · 1 Spot |
These are also defined in signer_client.py in the Python SDK, and in constants.go in the GO SDK.
Market order
// Buy 0.05 ETH, paying at most $3,150
const tx = check(lighter.SignCreateOrder(
0, clientOrderIndex, toInt(0.05, 4), toInt(3150, 2),
0, // buy
1, // MARKET
0, // IOC
0, 0,
0, // orderExpiry: 0 for IOC
0, 0, 0, 0, 0,
0, await nextNonce(), API_KEY_INDEX, ACCOUNT_INDEX,
));
await sendTx(tx);Pick price from the order book with a slippage buffer (e.g. best ask × 1.01 for buys). If the book can't fill at that price or better, the rest is cancelled.
Post-only limit order
const tx = check(lighter.SignCreateOrder(
0, clientOrderIndex, toInt(0.05, 4), toInt(2900, 2),
0, 0 /* LIMIT */, 2 /* POST_ONLY */, 0, 0,
Date.now() + 7 * 24 * 3600e3,
0, 0, 0, 0, 0,
0, await nextNonce(), API_KEY_INDEX, ACCOUNT_INDEX,
));Stop-loss and take-profit
Set triggerPrice and price. price is the worst fill price once triggered, i.e. the allowed slippage, with the same logic as taker orders. Use reduceOnly = 1 to protect a position.
To attach TP/SL to an entry in one go, use SignCreateGroupedOrders (OTO / OCO / OTOCO groupings). Grouped orders count as one transaction for quota. See WASM signer functions.
Modify
const tx = check(lighter.SignModifyOrder(
marketIndex, clientOrderIndex, newBaseAmount, newPrice, newTriggerPrice,
0, 0, 0, // integrator fields (keep the same as the original order)
0, 0, // self-trade modes
0, await nextNonce(),
0, // orderVersion: optional; if set, must be > current version
API_KEY_INDEX, ACCOUNT_INDEX,
));Order Version
You can specify an order_version when modifying orders, so that the sequencer will reject any version that is not strictly greater than the order's current stored version, returning AppErrInvalidOrderOrderVersion. This is optional, and examples can be found here (http) and here (ws). Order events will include order_version, and Trade events will include ask_order_version, bid_order_version.
Self-trade prevention
selfTradeBehaviorMode and selfTradeEqualityMode control what happens when your order would match another order of yours. The default 0, 0 expires the resting (maker) order when both orders come from the same account index. Other values are listed in Other order enums.
Not combinable with partner feesA non-default self-trade mode can't be combined with non-zero integrator fees on the same order (error
21159). If you charge partner fees, leave both self-trade modes at0.
Cancel
// One order
lighter.SignCancelOrder(marketIndex, clientOrderIndex, 0, nonce, API_KEY_INDEX, ACCOUNT_INDEX);
// All orders (timeInForce 0 = now; cancelAllMarketIndex 255 = all markets)
lighter.SignCancelAllOrders(0, 0, 255, 0, nonce, API_KEY_INDEX, ACCOUNT_INDEX);Batch
Send up to 50 transactions in one request. All transactions in a batch must use the same account and API key, with consecutive nonces:
const n = await nextNonce();
const txs = [0, 1, 2].map(i => check(lighter.SignCreateOrder(/* … */ 0, n + i, API_KEY_INDEX, ACCOUNT_INDEX)));
await fetch(`${BASE_URL}/api/v1/sendTxBatch`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
tx_types: JSON.stringify(txs.map(t => t.txType)),
tx_infos: JSON.stringify(txs.map(t => t.txInfo)),
}),
});- The whole batch waits for the longest speed bump of any transaction in it. Mixing a taker order into a batch of post-only orders delays all of them.
- Large batches: request-body size limits at the CDN edge can reject a large REST batch before it reaches the API (you'll get an HTTP error without Lighter's JSON body). REST batches of up to ~15 orders are safe. For bigger batches, use WebSocket
jsonapi/sendtxbatch(below), which accepts messages up to 64 KB, enough for 50 orders.
Python example: send_batch_tx_http.py.
Leverage and margin
// Initial margin fraction in basis points of 10,000: 10x leverage -> 1000
lighter.SignUpdateLeverage(marketIndex, 1000, 0 /* 0 cross, 1 isolated */, 0, nonce, API_KEY_INDEX, ACCOUNT_INDEX);
// Add/remove isolated margin
lighter.SignUpdateMargin(marketIndex, usdcAmount, direction /* 1 add, 0 remove */, 0, nonce, API_KEY_INDEX, ACCOUNT_INDEX);Sending over WebSocket
Lower latency than REST, same limits:
{ "type": "jsonapi/sendtx", "data": { "id": "my-req-1", "tx_type": 14, "tx_info": { … } } }jsonapi/sendtxbatch takes tx_types and tx_infos arrays (max 50). Messages are limited to 64 KB after decompression. permessage-deflate is supported, but it doesn't raise that limit. Full message format: Send Tx and Send Batch Tx.
Confirming execution
sendTx returning code: 200 only means the transaction was accepted. Subscribe to account_orders/{market}/{account} or account_tx/{account} to see fills and rejections. See Account data & positions.
Updated 13 days ago
