feat(clob): add wait_for_order_settlement for async order settlement - #177
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 623b084. Configure here.
| hashes = dict.fromkeys( | ||
| trade.transaction_hash for trade in trades if not _is_failed_trade(trade) | ||
| ) | ||
| return tuple(cast(TransactionHash, tx_hash) for tx_hash in hashes) |
There was a problem hiding this comment.
Empty settlement hashes returned
Medium Severity
_collect_settlement_hashes treats any non-FAILED trade as settled and includes its transaction_hash verbatim. The CLOB API is known to return empty transaction_hash strings, and unlike the relayer poll path—which raises UnexpectedResponseError when a confirmed transaction has no hash—this can surface "" as a TransactionHash to callers after a successful wait.
Reviewed by Cursor Bugbot for commit 623b084. Configure here.


Context
CLOB order settlement is moving to an asynchronous pipeline (Polymarket/clob-v2#299): once it rolls out, matched order responses no longer include
transactionsHashesand instead carry thetradeIDsof the fills, with hashes surfacing on the trades shortly after. The rollout is runtime-flag gated server-side, so both response shapes will coexist during the transition.Python port of Polymarket/ts-sdk#210. Tracked in DEV-418. Related legacy client change: Polymarket/clob-client-v2#90.
Design
Placement stays fast; settlement is an explicit, awaitable step — same design as the TypeScript SDK and consistent with the existing relayer transaction wait pattern. Blocking resolution inside
place_market_orderwas considered and rejected: it would silently re-add ~0.5–1s to the placement hot path and cannot represent timeout/failed-fill outcomes without new response surface anyway.Changes
SecureClient.wait_for_order_settlement(order, *, timeout_s=30.0)and theAsyncSecureClientequivalent — polls the order's trades (by the response'strade_ids) until every fill reaches a terminal state, then returns the settlement hashes astuple[TransactionHash, ...].()immediately. Settlement covers fills that occurred at placement; future fills of a resting order are followed via the user stream (documented).TimeoutErrorwhen fills are still settling at the deadline andTransactionFailedErrorwhen every fill failed execution. Partial failures return the settled hashes; failed fills contribute no hash._internal/actions/orders/settlement.pyfollowing the house sync/async pattern (thin duplicated loops over shared pure helpers, mirroring the relayer poll module), reusing the existing account-trades request builder/parser.AcceptedOrder/RejectedOrderdocumenting the best-effort semantics oftransactions_hashesand the fills-at-placement scope oftrade_ids.Testing
httpx.MockTransporton the sync client: sync short-circuit, no-fills, polling, partial failure, all-failed →TransactionFailedError, timeout →TimeoutError. The async variant shares the same pure helpers and differs only in transport/sleep.make checkclean: ruff, format, pyright, 1842 tests.Note
Medium Risk
New trading helper affects how integrators wait for settlement and interpret trade status; behavior is additive and placement is unchanged, but polling/timeouts and partial-failure semantics matter for production order flows.
Overview
Adds an optional, awaitable settlement step after order placement so callers can obtain on-chain transaction hashes when CLOB responses only include
trade_ids(async settlement rollout).SecureClient/AsyncSecureClientexposewait_for_order_fill_settlement(order, *, timeout_s=30)— polls account trades by the accepted order’strade_idsuntil each fill is terminal (CONFIRMEDorFAILED), then returns deduplicated settlement hashes. Notrade_ids→ returns existingtransactions_hashesimmediately (backward compatible). RaisesTimeoutErrororTransactionFailedErrorwhen all fills fail.Internal
_internal/actions/orders/settlement.pyholds shared sync/async polling (reuses account-trades list builder/parser).Models:
TradeStatus+ wire normalization (TRADE_STATUS_*vs plain) onClobTradeand user stream trades;AcceptedOrderdocs clarifytrade_ids/ best-efforttransactions_hashesscope (fills at placement only).Tests: unit coverage for polling, short-circuit, partial/total failure, timeout; integration order tests assert hashes after matched placement.
Reviewed by Cursor Bugbot for commit 980b1bf. Bugbot is set up for automated code reviews on this repo. Configure here.