client.trade.market_open / market_close /
increase_position in the SDK, and — with a locally built intent — the
market-maker fast path: local build + sign →
one POST, zero API round-trips before submission.
Endpoints
Request body
Why
eip7702 exists at all. The server decides which mechanism to execute
from its own configuration, never from the request — so operations can move
traffic between EIP-712 and EIP-7702 (or roll back) without a client release.
The SDK’s high-level calls send both legs to keep that flip free. A client
that only signs intents may omit eip7702 — that is the market-maker fast
path — with the trade-off that its requests start returning 400 if the
deployment is ever switched to EIP-7702 mode.Supported order types
Any value outside this set is rejected with400.
Notes:
- PnL vs non-PnL is a property of the pair — use the
_PNLtypes on PnL pairs only; mismatches are rejected. - Coin-exposure variants size the order in the base asset (e.g. exactly 0.5 ETH) instead of USDC notional.
- Increases have no PnL variant.
- The intent structs, field meanings, and units are documented on the
corresponding tx-builder pages (
/v2/intents/open,/v2/intents/close,/v2/intents/increase, and their-coinvariants) — themessageshown there is exactly what you sign, and the returnedencodedIntentis theuserIntentthis service consumes. Remember: deadlines are unix milliseconds, nonces are unordered 256-bit bitmap values, and closes bind the position’s on-chaintimestamp(PositionMismatchon drift).
The SSE stream
Read the POST response as a stream (fetch + reader; the browser
EventSource API is GET-only). Every request produces one accepted event,
zero or more non-terminal AttemptFailed events, one initiation event, and
exactly one terminal event:
Either flow can instead end with:
MarketOrderCanceled— the transaction succeeded but the protocol declined the fill (e.g. slippage). A market outcome, not a system error.Error— execution failed (bad intent, no price, retries exhausted) or the stream view timed out.
AttemptFailed (non-terminal, persisted) reports one execution attempt
that hit a retryable condition — payload {attempt, code, message, willRetry} — while the service keeps working the request. Treat unknown
event types as non-terminal noise: the server adds informational types
without a version bump.
AttemptFailed and Error payloads carry a machine-readable code next
to the human message: a bare Avantis contract error name (WrongSl,
HighSlippage, …) when the failure decoded to a specific revert, or a
synthetic service code (NO_PRICE, SPREAD_BLOCKED, SPREAD_UNAVAILABLE,
SUBMISSION_FAILED, ATTEMPTS_EXHAUSTED, TX_NOT_EXECUTED,
STREAM_TIMEOUT, ENQUEUE_FAILED, plus RELAY_FAILED / TX_REVERTED /
RELAY_TIMEOUT in EIP-7702 mode). Branch on code, not on the message.
Wire format: each event is an event: line (type), an id: line (the seq,
monotonic per trackingId), and a data: line (JSON payload), terminated by a
blank line. Lines starting with : are keep-alive comments (sent every ~15s)
— a spec-compliant parser never surfaces them. The stream has a hard lifetime
cap (60s in EIP-712 mode, 180s in EIP-7702 mode); a stream that ends without
a terminal event does not mean failure — replay by seq (below).
Event payloads
All uint values are strings in raw on-chain units (USDC 1e6, prices/leverage/exposure 1e10) — many exceedNumber.MAX_SAFE_INTEGER.
MarketOrderAccepted (seq 0) — the request was accepted and a
trackingId minted. Nothing is on-chain yet; save it for reconnects.
MarketOrderInitiated — the order is registered on-chain with an orderId.
IncreasePositionRequested — the increase is registered on-chain.
MarketOrderExecuted (terminal — opens/closes) — the final fill. t is
the stored Trade tuple (initialPosToken = final collateral, plus leverage,
openPrice, tp/sl). On closes, percentProfit and usdcSentToTrader
carry the realized outcome.
PositionSizeIncreased (terminal — increases) — note the shape differs
from a fill: no price / percentProfit / usdcSentToTrader, and t is the
blended resulting position.
MarketOrderCanceled (terminal) — the protocol declined the fill.
AttemptFailed (non-terminal) — one retryable attempt failed; the
service is still working the request.
Error (terminal — except STREAM_TIMEOUT, which is this connection’s
view expiring).
Replaying by trackingId
Every streamed event is persisted with itsseq, so a dropped stream is fully
recoverable:
afterSeqis optional — omit it for the full history.404= unknown trackingId. An emptyeventsarray = accepted but nothing has happened yet; poll again (~1–2s cadence) until a terminal event lands.
Error handling summary
Because intents carry unordered nonces, the service retries execution
internally without double-execution risk — never re-POST an accepted order;
only retry if the POST itself returned 4xx.
Using it from the SDK
on_event is available on every layer of the path — trade.market_* /
increase_position*, engine.submit_intent_batch, and
BatchedMarketClient.execute / .wait — sync or async callables; see
Track the order lifecycle.
See MM fast path for the full zero-I/O signing loop.