From b5ac5c2b793988abd560478dfcc711e91c13f00d Mon Sep 17 00:00:00 2001 From: icc Date: Sun, 24 May 2026 23:50:26 +0800 Subject: [PATCH 1/2] fix: expose Polymarket auth initialization in SDKs --- core/scripts/generate-python-exchanges.js | 13 ++++++++ sdks/python/pmxt/_exchanges.py | 5 +++ sdks/python/tests/test_public_exports.py | 27 ++++++++++++++++ sdks/typescript/pmxt/client.ts | 39 +++++++++++++++++++++++ 4 files changed, 84 insertions(+) diff --git a/core/scripts/generate-python-exchanges.js b/core/scripts/generate-python-exchanges.js index da21a1d4..dad23e35 100644 --- a/core/scripts/generate-python-exchanges.js +++ b/core/scripts/generate-python-exchanges.js @@ -18,6 +18,14 @@ const OVERRIDES = { polymarket: { // The Python SDK defaults to gnosis-safe to match historical behaviour. defaults: { signature_type: '"gnosis-safe"' }, + methods: [ + [ + ' def init_auth(self) -> None:', + ' """Initialize L2 API credentials for Polymarket implicit API signing."""', + ' self._call_method("initAuth")', + ' return None', + ].join('\n'), + ], }, myriad: { // Myriad uses privateKey as the wallet address, not a signing key. @@ -108,6 +116,7 @@ function generateClass(exchange) { const aliases = ov.paramAliases || {}; const defaults = ov.defaults || {}; const paramDocs = ov.paramDocs || {}; + const methods = ov.methods || []; const constructorParams = []; const superArgs = [`exchange_name="${name}"`]; @@ -207,6 +216,10 @@ function generateClass(exchange) { ); } + if (methods.length) { + lines.push('', ...methods); + } + return lines.join('\n'); } diff --git a/sdks/python/pmxt/_exchanges.py b/sdks/python/pmxt/_exchanges.py index 1a61bf0c..bc1bae3a 100644 --- a/sdks/python/pmxt/_exchanges.py +++ b/sdks/python/pmxt/_exchanges.py @@ -58,6 +58,11 @@ def _get_credentials_dict(self) -> Optional[Dict[str, Any]]: creds["passphrase"] = self.passphrase return creds if creds else None + def init_auth(self) -> None: + """Initialize L2 API credentials for Polymarket implicit API signing.""" + self._call_method("initAuth") + return None + class Limitless(Exchange): """Limitless exchange client.""" diff --git a/sdks/python/tests/test_public_exports.py b/sdks/python/tests/test_public_exports.py index 852a9080..e1896959 100644 --- a/sdks/python/tests/test_public_exports.py +++ b/sdks/python/tests/test_public_exports.py @@ -67,3 +67,30 @@ def test_legacy_polymarket_us_alias_stays_public(): assert "Polymarket_us" in exchange_imports assert "Polymarket_us" in public_exports assert aliases["Polymarket_us"] == "PolymarketUS" + + +def test_polymarket_init_auth_is_generated(): + exchanges_path = Path(__file__).resolve().parents[1] / "pmxt" / "_exchanges.py" + tree = ast.parse(exchanges_path.read_text(encoding="utf-8")) + + polymarket_class = next( + node + for node in tree.body + if isinstance(node, ast.ClassDef) and node.name == "Polymarket" + ) + init_auth = next( + node + for node in polymarket_class.body + if isinstance(node, ast.FunctionDef) and node.name == "init_auth" + ) + + call = next( + node + for node in init_auth.body + if isinstance(node, ast.Expr) and isinstance(node.value, ast.Call) + ) + assert isinstance(call, ast.Expr) + assert isinstance(call.value, ast.Call) + assert isinstance(call.value.func, ast.Attribute) + assert call.value.func.attr == "_call_method" + assert call.value.args[0].value == "initAuth" diff --git a/sdks/typescript/pmxt/client.ts b/sdks/typescript/pmxt/client.ts index 9979d3f8..a1525495 100644 --- a/sdks/typescript/pmxt/client.ts +++ b/sdks/typescript/pmxt/client.ts @@ -607,6 +607,28 @@ export abstract class Exchange { return response.json(); } + /** + * Dispatch a sidecar POST method with positional args and credentials. + * + * @internal - shared transport for hand-maintained methods that should + * never use the GET read path. + */ + protected async sidecarPostRequest(methodName: string, args: unknown[]): Promise { + const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/${methodName}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() }, + body: JSON.stringify({ args, credentials: this.getCredentials() }), + }); + if (!response.ok) { + const body = await response.json().catch(() => ({})); + if (body.error && typeof body.error === "object") { + throw fromServerError(body.error); + } + throw new PmxtError(body.error?.message || response.statusText); + } + return response.json(); + } + // BEGIN GENERATED METHODS async loadMarkets(reload: boolean = false): Promise> { @@ -2446,6 +2468,23 @@ export class Polymarket extends Exchange { }; super("polymarket", polyOptions as ExchangeOptions); } + + /** + * Initialize Polymarket L2 API credentials for implicit API signing. + * + * Call this before private Polymarket implicit-API endpoints when the + * underlying CLOB credentials have not been created yet. + */ + async initAuth(): Promise { + await this.initPromise; + try { + const json = await this.sidecarPostRequest('initAuth', []); + this.handleResponse(json); + } catch (error) { + if (error instanceof PmxtError) throw error; + throw new PmxtError(`Failed to initAuth: ${error}`); + } + } } /** From 8bb5aba267c84ac668451ca5eb23cc31f97f9011 Mon Sep 17 00:00:00 2001 From: icc Date: Sun, 24 May 2026 23:56:07 +0800 Subject: [PATCH 2/2] chore: sync generated SDK surfaces --- sdks/python/API_REFERENCE.md | 3 +++ sdks/python/pmxt/client.py | 28 ++++++++++++++-------------- sdks/typescript/API_REFERENCE.md | 3 +++ sdks/typescript/pmxt/client.ts | 2 +- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/sdks/python/API_REFERENCE.md b/sdks/python/API_REFERENCE.md index d49b9183..37a4a598 100644 --- a/sdks/python/API_REFERENCE.md +++ b/sdks/python/API_REFERENCE.md @@ -1927,6 +1927,7 @@ similarity_threshold: float # For semantic search (used by Limitless) class EventFetchParams: query: str # For keyword search limit: float # Maximum number of results to return +cursor: str # Opaque venue pagination cursor, where supported. offset: float # Pagination offset — number of results to skip sort: str # Sort order for results status: str # Filter by event status (default: 'active', 'inactive' and 'closed' are interchangeable) @@ -3367,6 +3368,8 @@ Update Order Group Limit *(Auth required)* Get Balance *(Auth required)* +**Parameters:** +- `subaccount` (query, integer) — Subaccount number (0 for primary, 1-32 for subaccounts). When provided, returns only that subaccount's balance. --- ##### `CreateSubaccount` diff --git a/sdks/python/pmxt/client.py b/sdks/python/pmxt/client.py index ea231efb..0a88bbf2 100644 --- a/sdks/python/pmxt/client.py +++ b/sdks/python/pmxt/client.py @@ -751,7 +751,7 @@ def fetch_markets(self, params: Optional[dict] = None, **kwargs) -> List[Unified if kwargs: params = {**(params or {}), **kwargs} if params is not None: - args.append(_convert_params_to_camel(params)) + args.append(params) body: dict = {"args": args} creds = self._get_credentials_dict() if creds: @@ -774,7 +774,7 @@ def fetch_markets_paginated(self, params: Optional[dict] = None, **kwargs) -> Pa if kwargs: params = {**(params or {}), **kwargs} if params is not None: - args.append(_convert_params_to_camel(params)) + args.append(params) body: dict = {"args": args} creds = self._get_credentials_dict() if creds: @@ -801,7 +801,7 @@ def fetch_events(self, params: Optional[dict] = None, **kwargs) -> List[UnifiedE if kwargs: params = {**(params or {}), **kwargs} if params is not None: - args.append(_convert_params_to_camel(params)) + args.append(params) body: dict = {"args": args} creds = self._get_credentials_dict() if creds: @@ -824,7 +824,7 @@ def fetch_market(self, params: Optional[dict] = None, **kwargs) -> UnifiedMarket if kwargs: params = {**(params or {}), **kwargs} if params is not None: - args.append(_convert_params_to_camel(params)) + args.append(params) body: dict = {"args": args} creds = self._get_credentials_dict() if creds: @@ -847,7 +847,7 @@ def fetch_event(self, params: Optional[dict] = None, **kwargs) -> UnifiedEvent: if kwargs: params = {**(params or {}), **kwargs} if params is not None: - args.append(_convert_params_to_camel(params)) + args.append(params) body: dict = {"args": args} creds = self._get_credentials_dict() if creds: @@ -1144,7 +1144,7 @@ def close(self) -> None: except ApiException as e: raise self._parse_api_exception(e) from None - def fetch_market_matches(self, params: Optional[dict] = None, **kwargs) -> List[MatchResult]: + def fetch_market_matches(self, params: Optional[dict] = None, **kwargs) -> List[Any]: try: args = [] if kwargs: @@ -1167,7 +1167,7 @@ def fetch_market_matches(self, params: Optional[dict] = None, **kwargs) -> List[ except ApiException as e: raise self._parse_api_exception(e) from None - def fetch_matches(self, params: dict, **kwargs) -> List[MatchResult]: + def fetch_matches(self, params: dict, **kwargs) -> List[Any]: try: args = [] if kwargs: @@ -1189,7 +1189,7 @@ def fetch_matches(self, params: dict, **kwargs) -> List[MatchResult]: except ApiException as e: raise self._parse_api_exception(e) from None - def fetch_event_matches(self, params: Optional[dict] = None, **kwargs) -> List[EventMatchResult]: + def fetch_event_matches(self, params: Optional[dict] = None, **kwargs) -> List[Any]: try: args = [] if kwargs: @@ -1212,7 +1212,7 @@ def fetch_event_matches(self, params: Optional[dict] = None, **kwargs) -> List[E except ApiException as e: raise self._parse_api_exception(e) from None - def compare_market_prices(self, params: dict, **kwargs) -> List[PriceComparison]: + def compare_market_prices(self, params: dict, **kwargs) -> List[Any]: try: args = [] if kwargs: @@ -1234,7 +1234,7 @@ def compare_market_prices(self, params: dict, **kwargs) -> List[PriceComparison] except ApiException as e: raise self._parse_api_exception(e) from None - def fetch_related_markets(self, params: dict, **kwargs) -> List[MatchResult]: + def fetch_related_markets(self, params: dict, **kwargs) -> List[Any]: try: args = [] if kwargs: @@ -1256,7 +1256,7 @@ def fetch_related_markets(self, params: dict, **kwargs) -> List[MatchResult]: except ApiException as e: raise self._parse_api_exception(e) from None - def fetch_matched_markets(self, params: Optional[dict] = None, **kwargs) -> List[MatchResult]: + def fetch_matched_markets(self, params: Optional[dict] = None, **kwargs) -> List[Any]: try: args = [] if kwargs: @@ -1279,7 +1279,7 @@ def fetch_matched_markets(self, params: Optional[dict] = None, **kwargs) -> List except ApiException as e: raise self._parse_api_exception(e) from None - def fetch_matched_prices(self, params: Optional[dict] = None, **kwargs) -> List[PriceComparison]: + def fetch_matched_prices(self, params: Optional[dict] = None, **kwargs) -> List[Any]: try: args = [] if kwargs: @@ -1302,7 +1302,7 @@ def fetch_matched_prices(self, params: Optional[dict] = None, **kwargs) -> List[ except ApiException as e: raise self._parse_api_exception(e) from None - def fetch_hedges(self, params: dict, **kwargs) -> List[PriceComparison]: + def fetch_hedges(self, params: dict, **kwargs) -> List[Any]: try: args = [] if kwargs: @@ -1324,7 +1324,7 @@ def fetch_hedges(self, params: dict, **kwargs) -> List[PriceComparison]: except ApiException as e: raise self._parse_api_exception(e) from None - def fetch_arbitrage(self, params: Optional[dict] = None, **kwargs) -> List[ArbitrageOpportunity]: + def fetch_arbitrage(self, params: Optional[dict] = None, **kwargs) -> List[Any]: try: args = [] if kwargs: diff --git a/sdks/typescript/API_REFERENCE.md b/sdks/typescript/API_REFERENCE.md index b4ada98d..44b6bce9 100644 --- a/sdks/typescript/API_REFERENCE.md +++ b/sdks/typescript/API_REFERENCE.md @@ -1927,6 +1927,7 @@ similarityThreshold?: number; // For semantic search (used by Limitless) interface EventFetchParams { query?: string; // For keyword search limit?: number; // Maximum number of results to return +cursor?: string; // Opaque venue pagination cursor, where supported. offset?: number; // Pagination offset — number of results to skip sort?: string; // Sort order for results status?: string; // Filter by event status (default: 'active', 'inactive' and 'closed' are interchangeable) @@ -3368,6 +3369,8 @@ Update Order Group Limit *(Auth required)* Get Balance *(Auth required)* +**Parameters:** +- `subaccount` (query, integer) — Subaccount number (0 for primary, 1-32 for subaccounts). When provided, returns only that subaccount's balance. --- ##### `CreateSubaccount` diff --git a/sdks/typescript/pmxt/client.ts b/sdks/typescript/pmxt/client.ts index a1525495..4fa8fcb2 100644 --- a/sdks/typescript/pmxt/client.ts +++ b/sdks/typescript/pmxt/client.ts @@ -687,7 +687,7 @@ export abstract class Exchange { } } - async fetchMarketsPaginated(params?: MarketFetchParams): Promise { + async fetchMarketsPaginated(params?: any): Promise { await this.initPromise; try { const args: any[] = [];