place_order, cancel_order, get_order, open_orders, and order_trades implemented on SigningClient (Plan 3a, Task 1, branch autotrade-foundation, crates/polymarket-clob/src/lib.rs). Includes a critical serde landmine in the vendored SDK and the fix.
KEY FINDING — SignedOrder Is Not Deserializable
The original task spec called for round-tripping BuiltOrder.signed_payload through serde:
// DOES NOT COMPILE — SignedOrder has no Deserialize
serde_json::from_value::<SignedOrder>(built.signed_payload.0.clone())SignedOrder (vendored rs-clob-client-v2) derives only #[derive(Debug, Builder, PartialEq)] — no Serialize, no Deserialize.
Fix: Hold the Typed Value Directly
BuiltOrder was redesigned to hold the typed value instead of a serialized Value:
| Before | After |
|---|---|
signed_payload: SignedPayload(serde_json::Value) | signed_order: SignedOrder |
SignedPayload wrapper struct existed | SignedPayload struct dropped entirely |
BuiltOrder now carries the typed SignedOrder through to place_order, which passes it directly to the vendored client without any serde round-trip.
Do not attempt to serialize/deserialize SignedOrder
The vendored type has no serde derives. Any code path requiring a
Valuerepresentation must avoidSignedOrderas an intermediate — build a separate serializable struct if needed.
Vendored API Shapes (Verified)
OrdersRequest
- Derives
Default; all fields areOption<_> - Use
OrdersRequest::default()to request all open orders (no filter)
TradesRequest
- Derives
Default; all fields areOption<_> - Use
TradesRequest::default()to request all trades (no filter)
Pagination
Page<T>.data: Vec<T>
The paginated data lives in the .data field — not .items, .results, or .orders.
OpenOrderResponse Fields
| Field | Type | Notes |
|---|---|---|
.id | String | CLOB order id |
.status | OrderStatusType | Implements Display |
.size_matched | Decimal | Filled portion |
TradeResponse Fields
| Field | Type | Notes |
|---|---|---|
.id | String | Trade id |
.taker_order_id | String | CLOB order id (not EIP-712 hash) |
.status | TradeStatusType | Implements Display |
.size | Decimal | Trade size |
.price | Decimal | Trade price |
.transaction_hash | B256 | On-chain tx hash |
TradeResponse has NO order_hash
TradeResponsecarries no EIP-712 hash.TradeView.order_hashis alwaysNone— the field is populated client-side only by filteringtaker_order_id == clob_order_id. Do not attempt to join on EIP-712 hashes when looking up trades for an order.
PostOrderResponse Fields
| Field | Type | Notes |
|---|---|---|
.order_id | String | serde alias "orderID" (camelCase in JSON) |
.status | OrderStatusType | Implements Display |
.making_amount | Decimal | |
.taking_amount | Decimal | |
.transaction_hashes | Vec<B256> | On-chain tx hashes |
.trade_ids | Vec<String> |
orderID alias
The JSON key is
"orderID"(camelCase). The Rust field is.order_id(snake_case).serde(alias = "orderID")handles the mapping — access the field via.order_idin code, not.orderID.
cancel_order Return Type
cancel_order(&str) -> Result<CancelOrdersResponse>The response is discarded in the current implementation — cancel_order returns (). The CancelOrdersResponse carries confirmation data but is not needed for the autotrade flow.
Gate
cargo fmt + cargo build + 265 tests all pass, EXIT:0.