local_intents() removes that: the LocalIntentBuilder mirrors the on-chain EIP-712 schemas (proven byte-for-byte by the golden-vector test suite), so intents are built and signed in microseconds with no I/O.
The batched-market service’s EIP-7702 leg is optional: a signed intent alone executes. The hot path is therefore local build + sign → batched-market POST, with zero API round-trips before submission. (High-level SDK calls like trade.market_open still attach a pre-signed EIP-7702 transaction alongside the intent, letting the server pick the execution mechanism; pass calldata= to submit_intent_batch if you want that from the fast path too.)
The example below sizes the order in coin units (exactly 0.5 ETH), the usual
shape for market-making flow; the fill leverage floats within
[min_leverage, max_leverage]. For a USDC-sized open, use
builder.open_trade(...) with /v2/trade/open and
AggregatorOrderType.MARKET_OPEN instead.
Global TP/SL updates (
UpdateTpSlReq) are also built and signed fully
locally, but ride a different rail: the signed intent goes to the core API
price-triggers endpoint (PUT /price-triggers/global-...), which executes
the operator entry point itself. They settle by re-reading the position
(what trade.update_tp_sl(wait=True) does), not by tracking_id.Builder surface
Payloads are
IntentPayload objects identical to what the tx-builder would return, and go through the same digest-verified signer.
Since the builder never touches the network, prices are always caller-supplied: coin/increase helpers take an explicit open_price/wanted_price (the tx-builder route would resolve these from the feed). Note partial_tp_sl and update_tp_sl only build intents; the signed payloads still have to be submitted to the core API /price-triggers (what client.trade.partial_tp_sl / client.trade.update_tp_sl do), not to the batched-market endpoint. Likewise twap_* intents go to the TWAP API (what client.trade.twap_* does).
Settling
wait=False returns as soon as the order is accepted, but an accepted order can still fail (declined fill, on-chain revert). You own the settlement check, off the hot path.
Market orders (batched-market route) settle by tracking_id:
MarketOrderExecuted event carries the full fill — execution price, positionSizeUSDC, percentProfit, usdcSentToTrader, and the stored trade tuple t with the final collateral (initialPosToken), leverage, open price and TP/SL — so reconciliation needs no follow-up positions query. Numerics are strings in raw on-chain units (1e6 USDC / 1e10 prices-leverage); see the payload reference.
engine.batched_market.status(tracking_id, after_seq=n) is the non-blocking single poll; every event carries a seq, so a crashed process can resume replay without missing or double-counting events.
Prefer the SDK’s settle logic but want the journey as it happens — e.g. AttemptFailed diagnostics as debug logs? Pass on_event= to submit_intent_batch (with wait=True) or to batched_market.wait(...): the hook fires per lifecycle event, terminal included even when the call raises, while settlement still returns/raises through the SDK. See Track the order lifecycle.
Blitz relayer routes (passthrough actions: limit orders, margin, approvals, …) settle by request_id:
engine.relayer.status(request_id) is the single non-blocking poll returning a RelayStatus (settled, success, tx_hash, error_message). This is exactly what the default wait=True path runs internally.
Pair with
wait=Falseon submission, confirming fills via the settlement calls above or the order event stream.- The Lazer SSE price stream for the freshest
open_price.