Exchange is a dYdX order-status and rejected-trade recovery workflow
Exchange is a dYdX order-status and rejected-trade recovery workflow that separates a signed request from an executed perpetual order. Start by matching the ticker, side, size, price boundary, time-in-force, and subaccount to the intended trade. The dYdX Chain then sends the instruction to its central limit order book, while the Indexer reports the order and any fills. A wallet approval confirms only the signature, not the fill. Recovery therefore begins with total filled quantity, terminal status, and remaining size. Immediate-or-Cancel requests cancel their unmatched portion; resubmit only that residual after recalculating the price limit, collateral, and reduce-only effect. This page follows that sequence from input validation through confirmation and controlled retry.
From quote fields to signed broadcast
Six fields - ticker, side, size, price boundary, time-in-force, and subaccount - define the request before one wallet signature authorizes a validator broadcast on dYdX Chain.
Read the ticket from the market outward. The ticker and CLOB pair ID select the perpetual. BUY or SELL fixes direction; size fixes base quantity; the limit price sets the worst acceptable price. IOC, Post Only, or unspecified time-in-force then determines whether unmatched quantity disappears, rests, or must provide maker liquidity. Finally, reduce-only ties execution to an existing position, while the subaccount chooses the collateral ledger. If any field changes in the wallet prompt, cancel the prompt and rebuild the order from the intended ticket.
Quote and market fields
Market metadata converts human-readable BTC-USD or ETH-USD amounts into base quantums and prices into subticks. Size must be a multiple of
stepBaseQuantums, while price must be a multiple of
subticksPerTick. Fetch both values after a market configuration change because cached granularity produces a rejection before matching begins.
Signed order path
The wallet signs an exact Cosmos SDK transaction. The Node API broadcasts it to a validator, while the read-only Indexer API later exposes status, size, filled quantity, and update height. Cosmos SDK and CometBFT separate transaction admission from consensus execution.
Short-term requests
Order flag 0 identifies a short-term placement. It carries a Good-Till-Block height, contains exactly one
MsgPlaceOrder
message, and enters validator memory; only a match commits its fill and expiry data.
Stateful requests
Order flag 64 identifies a long-term order, while flag 32 identifies a conditional order. Both use Good-Till-Block-Time, and the protocol window reaches 95 days. A long-term placement included in block N becomes available for taker matching in block N+1. Choose the lifetime before choosing recovery timing.
CLOB market selection versus pool routing
One CLOB pair ID selects the dYdX market, so pool paths and multi-hop routes stay outside the signed instruction and its rejection-recovery logic for each order.
This mechanism differs from an automated market maker swap. Uniswap v3 selects a pool and fee tier; 1inch searches routes across liquidity venues; Jupiter evaluates Solana paths. dYdX Chain instead matches a perpetual order inside the selected CLOB, using price-time priority at book levels. A rejected order therefore needs a valid CLOB pair, an acceptable limit, and book liquidity on the opposite side. The distinction prevents a swap-style reroute from changing the intended perpetual exposure.
BTC-USD and ETH-USD illustrate the selection rule: the ticker names the contract, and USDC supplies the quote denomination. Once the market is correct, the next decision is the price boundary and time-in-force.
What does each dYdX order status mean?
Seven Indexer statuses describe the dYdX order lifecycle: Open, Filled, Canceled, BestEffortCanceled, Untriggered, BestEffortOpened, and Pending, each with a different consequence for retry decisions.
Open means active and eligible for matching. Filled means filled quantity equals requested size. Canceled is terminal. Untriggered belongs to a conditional order that has not crossed its trigger. BestEffortOpened and BestEffortCanceled describe node-level updates for short-term orders before network-wide certainty. Pending marks processing before a stable order state appears. Treat only Filled or Canceled as a final instruction to stop retry logic; every other value calls for another reconciliation pass.
Partial execution appears in
totalFilled, not as a separate status among those 7 values. An Open order with a positive filled amount still owns residual size, while an IOC order moves its unmatched portion to cancellation. Read status and quantity together before changing exposure.
IOC remainders and fill accounting
Two values decide an IOC recovery: requested size and total filled; their difference is the unmatched amount that the matching engine cancels immediately rather than resting.
The default short-term window is 20 blocks, which the trading interface estimates at about 30 seconds.
Compute the resubmission amount as requested size minus
totalFilled, then reconcile that amount against fill records and the updated position. Never reuse the original requested size after a partial fill, because the second order would seek exposure that already executed. Refresh the order book before rebuilding the price boundary; IOC reaches only maker quantity available within that boundary at placement.
Reduce-only changes the final check. The residual must fit the remaining position, since the protocol resizes or cancels quantity that would cross through zero. The next signature should represent the remaining close, not the earlier ticket.
Rebuilding orders after protocol rejection
Five rejection families explain most failed placements on dYdX Chain: quantity units, price increments, expiry, execution flags, and collateral or position constraints during order validation.
Protocol codes 4, 5, 6, and 8 isolate invalid base quantums, an invalid Good-Till-Block value, invalid subticks, and an unknown CLOB pair. Code 3000 identifies invalid order flags. These are construction errors, so repeating the same payload reproduces the rejection. Fetch the market object, latest block height, and intended order type, then rebuild the serialized message. A wallet prompt that displays the corrected values should replace the rejected payload rather than merely signing it again. Confirm that the Node API and Indexer describe the same deployment before the next broadcast.
Quantity and price normalization
The market-specific
stepBaseQuantums
and
subticksPerTick
values control accepted granularity. Round in the direction that preserves the chosen price boundary, then recalculate size from normalized units. A decimal that looks valid in the interface still fails when its integer representation misses either multiple.
Execution and position constraints
Codes 2001 and 2002 mean reduce-only would increase the position or change its side. Code 2003 means Post Only would cross resting liquidity, while code 2004 records an IOC whose remaining size was canceled after immediate execution. Refresh the position and book before changing flags. Select the field that matches the returned code, rebuild once, and then reassess available collateral.
When should a rejected dYdX order be resubmitted?
One corrected residual should be resubmitted only after the earlier order reaches a terminal status or its best-effort state has been reconciled against fill records.
Create a fresh client ID for a deliberate new placement after the original becomes Filled, Canceled, or expired. The client ID is a 32-bit integer within the order ID, alongside subaccount, order flags, and CLOB pair ID. Reusing an active identity collides with replay protection or an existing stateful order. BestEffortOpened and BestEffortCanceled require patience: query fills and the order again instead of treating the node-level update as final. Related details are covered in Dydx exchange overview.
Stateful messages also consume strictly increasing Cosmos SDK sequence numbers. When several placements leave concurrently through different validators, arrival order can differ and a later sequence can fail. Send related stateful orders through one validator, serialize broadcasts, and retry only the message whose preceding sequence reached consensus.
Indexer confirmation and wallet-side evidence
Three records settle an order dispute: the validator broadcast response, the Indexer order object, and fill records that update the subaccount position after confirmed matching.
The broadcast response proves admission to the selected node, not a fill. The Indexer order object supplies status, size,
totalFilled, created height, updated height, and time-in-force. Fill records supply executed size and price. The position then confirms whether those fills changed long or short exposure as intended.
A REST response with status 200 returns the requested object, 400 identifies a malformed request, and 404 means that the Indexer lookup found no order. A 404 immediately after broadcast is not a terminal chain status. Requery by the exact order ID and compare the latest indexed height with the validator height.
WebSocket monitoring adds two timing rules. The service sends a ping every 30 seconds and disconnects after 10 seconds without a pong. Its default limit also allows 2 subscriptions per connection, channel, and channel ID each second. Reconnect, resubscribe, and backfill through REST before acting on a missed update.
The five-check resubmission gate
Five checks should pass before a retry leaves the wallet, because a corrected residual still fails when its market, precision, expiry, or position context is stale.
- The address, subaccount number, client ID, order flag, and CLOB pair reproduce the intended order ID.
-
Size aligns with
stepBaseQuantums, and price aligns withsubticksPerTickfor that market. -
totalFilledagrees with fill records and the latest position before any residual is calculated. - IOC, Post Only, and reduce-only behavior matches the desired urgency, liquidity role, and position direction.
- A fresh client ID, valid expiry, synchronized sequence, and sufficient collateral support the replacement.
If one condition fails, correct only the owning input and rebuild the signed message. When all 5 pass, use the latest order book to set the new price boundary and submit the residual once.
Exchange recovery for manual and automated orders
Two operating styles benefit from Exchange recovery discipline: manual order entry during fast markets and automated execution that must reconcile every state transition before retrying.
Manual traders gain a reproducible path when the interface shows a rejection, a partial fill, or a disappearing IOC balance. Automated systems gain an idempotent state machine: construct, sign, broadcast, reconcile, and retry. Python, TypeScript, and Rust clients all expose the same underlying separation between Node API writes and Indexer reads. The discipline matters most when order frequency rises, because stale positions and duplicate residuals compound quickly. End each cycle with one durable record containing the order ID, requested size, total filled, terminal status, and resulting position.
Exchange: frequently asked questions
-
Can two dYdX orders use the same client ID?
- No active orders on the same subaccount and CLOB pair should share a client ID. The 32-bit client ID forms part of the order ID with the subaccount, order flags, and CLOB pair ID. Reuse creates ambiguity and can collide with replay protection or an existing stateful placement. Give each deliberate retry a fresh ID after reconciling the earlier order's terminal status, fills, and expiry.
-
Does declining the wallet prompt create a dYdX order record?
- No, declining the wallet prompt leaves the order unsigned and prevents its broadcast. The Indexer therefore has no placement or fill to record from that attempt. Before opening another prompt, compare the displayed ticker, side, size, price boundary, expiry, and subaccount with the intended ticket. A missing record is expected in this case, while a signed broadcast with a temporary missing Indexer record requires a height check and another query. Use the validator response to distinguish the two paths before retrying the order.
-
Which expiry field belongs on a short-term dYdX order?
- A short-term dYdX order uses Good-Till-Block, not Good-Till-Block-Time. Its expiry cannot exceed the latest block height plus the 20-block short window. Stateful long-term and conditional orders use the timestamp-based field instead. Refresh the block height immediately before construction, because a valid value ages while the message waits. An expired payload should be rebuilt with a fresh client ID and current height before signing again for broadcast.
-
Is Post Only suitable for an urgent recovery trade?
- Post Only is unsuitable when the recovery order must execute against existing liquidity immediately. That instruction requires the order to rest as maker liquidity, and protocol code 2003 rejects it when its price would cross a resting maker order. Use IOC when immediate matching and cancellation of the remainder fit the intent. Keep Post Only when queue placement matters more than urgency, then choose a non-crossing price and monitor the Open status before making any further change to the order.
-
When does a conditional dYdX order remain Untriggered?
- A conditional order remains Untriggered until its configured trigger condition is met. It already exists as a stateful order, so this status does not justify resubmission. Confirm the trigger price, side, condition direction, and Good-Till-Block-Time, then wait for the Indexer transition to Open or a terminal status. Replacing it prematurely creates another instruction rather than repairing the original condition within the same subaccount and market.
-
Where does the executed price appear after a partial fill?
- Executed prices appear in fill records, while the order object retains its submitted price and cumulative total filled. Query fills for the same subaccount and order ID, then aggregate their executed sizes before calculating the residual. The resulting position provides a separate exposure check. An Open status alone does not reveal the prices of completed portions, and an IOC cancellation does not erase those fills. Store the fill identifiers, prices, sizes, and update heights with the terminal order record so the next recovery decision starts from reconciled execution data.