Skip to content

feat: requester-side combo RFQ support (request quote, accept, wait for fill) - #216

Merged
kartojal merged 6 commits into
mainfrom
feature/dev-425-python-sdk-port-combos-requester-support
Aug 13, 2026
Merged

feat: requester-side combo RFQ support (request quote, accept, wait for fill)#216
kartojal merged 6 commits into
mainfrom
feature/dev-425-python-sdk-port-combos-requester-support

Conversation

@kartojal

@kartojal kartojal commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Implements DEV-425, porting the requester-side combo RFQ surface from the TypeScript SDK (Polymarket/ts-sdk#251). Adds request_combo_quote, accept_combo_quote, wait_for_combo_fill, and fetch_rfq_status to SecureClient and AsyncSecureClient, backed by a new builder-gateway transport that attaches POLY_* L2 headers plus POLY_BUILDER_* headers on mutating requests (status reads are account-auth only).

Adaptations per the ticket: the BUY/SELL direction-sizing coupling is enforced at runtime via keyword arguments (amount= for BUY collateral, size= for SELL shares); RfqRejectionCode is a StrEnum with unknown-code passthrough; accept_combo_quote takes the ComboQuoteResult from the quote request since the status endpoint never returns the bundle. Business outcomes (no quotes, maker declined, expired acceptance window) are returned as result values, not raised.

Linear: DEV-425


Note

High Risk
New trading path signs orders and moves real funds on accept/fill; auth and idempotent accept retry behavior are security- and correctness-sensitive despite strong validation.

Overview
Adds requester-side combo RFQ to SecureClient / AsyncSecureClient: request_combo_quote, accept_combo_quote, wait_for_combo_fill, and fetch_rfq_status, implemented in new combo_rfq actions against /v1/builder/rfq/requests. Secure clients gain a builder_gateway transport (builder_gateway_url in env config) with L2 account headers plus Builder API key headers on non-GET calls; a Builder API key is required for quote/accept.

accept_combo_quote signs a v3 GTC order from a self-contained ComboQuote, retries once on ambiguous transport/parse failures, polls through maker last-look, and maps declines/expiry to returned failed outcomes (not exceptions). RequestRejectedError now surfaces JSON code; RFQ flows raise RfqRequestRejectedError with RfqRejectionCode where known.

Public polymarket.rfq grows models/enums (ComboQuote, status/fill/accept results, RfqStatus, etc.) and re-exports. Unit and metered integration tests cover parsing, echo validation, retries, and optional live quote/fill paths via POLYMARKET_COMBO_LEG_POSITION_IDS.

Reviewed by Cursor Bugbot for commit 168f1a9. Bugbot is set up for automated code reviews on this repo. Configure here.

@kartojal
kartojal marked this pull request as ready for review August 10, 2026 15:26
@kartojal
kartojal requested a review from naruto11eth August 10, 2026 15:47

@naruto11eth naruto11eth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really solid port overall - ts-sdk is mirrored well. Two things I'd like fixed before merge though, both on the execution path:

  • The quote isn't bound to the request that minted it. ts validates the echoed direction/side/leg_position_ids/requested_size before building the portable quote (rfq.ts:826-858) and derives the quote's direction from the validated echo; here _parse_combo_quote_result checks only the nested rfq_id and stamps the caller's direction onto whatever came back. Note when porting the fix: ts compares legs index-by-index against the canonically sorted request, so _validate_legs needs the same BigInt-ascending sort or the binding will false-positive.
  • SELL quotes drop net_receive_e6. The ts bindings model it, the client requires it for SELL (rfq.ts:797-802 throws when absent), and it's what tells a seller their exact post-fee proceeds before accepting - taker_amount_e6 is gross. ComboQuote needs the field, required for SELL, with a real SELL fixture.

Two smaller ones:

  • The accept retry covers TransportError only; ts broadened it to also retry UnexpectedResponseError (288f72b, landed after your head, so this is twin catch-up not a miss) - without it a garbled 200 after server-side acceptance raises while a live-funds order is already executing, instead of re-posting and reading the persisted status.
  • test_combo_rfq_live.py:44 parses POLYMARKET_COMBO_LEG_POSITION_IDS with a file-local helper; AGENTS.md wants that in tests/integration/conftest.py.

@kartojal
kartojal requested a review from brunson-bot August 13, 2026 10:00

@mcamara mcamara left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All @naruto11eth 's checks have been solved

@kartojal
kartojal dismissed naruto11eth’s stale review August 13, 2026 14:30

all checks resolved

@kartojal
kartojal merged commit e060f34 into main Aug 13, 2026
7 checks passed
@kartojal
kartojal deleted the feature/dev-425-python-sdk-port-combos-requester-support branch August 13, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants