diff --git a/core/src/exchanges/hyperliquid/utils.ts b/core/src/exchanges/hyperliquid/utils.ts index 4675e4d0..80b36b06 100644 --- a/core/src/exchanges/hyperliquid/utils.ts +++ b/core/src/exchanges/hyperliquid/utils.ts @@ -71,14 +71,26 @@ export function toMarketId(outcomeId: number): string { } /** - * Extract the numeric outcome ID from our market ID format. + * Extract the numeric outcome ID from a Hyperliquid identifier. + * + * Accepts either: + * - our canonical market ID, "hl-outcome-{N}" + * - a raw encoded asset token (numeric string >= OUTCOME_ASSET_BASE), + * as returned in UnifiedMarket.outcomes[].outcomeId. Decoded via + * decodeAssetId so callers can pass an outcome token directly. */ export function fromMarketId(marketId: string): number { const match = marketId.match(/^hl-outcome-(\d+)$/); - if (!match) { - throw new Error(`Invalid Hyperliquid market ID: ${marketId}`); + if (match) { + return parseInt(match[1], 10); + } + if (/^\d+$/.test(marketId)) { + const assetId = parseInt(marketId, 10); + if (assetId >= OUTCOME_ASSET_BASE) { + return decodeAssetId(assetId).outcomeId; + } } - return parseInt(match[1], 10); + throw new Error(`Invalid Hyperliquid market ID: ${marketId}`); } /** diff --git a/sdks/python/pmxt/client.py b/sdks/python/pmxt/client.py index cca8c805..495b90f2 100644 --- a/sdks/python/pmxt/client.py +++ b/sdks/python/pmxt/client.py @@ -1346,6 +1346,33 @@ def fetch_markets_paginated(self, params: Optional[dict] = None, **kwargs) -> Pa except ApiException as e: raise self._parse_api_exception(e) from None + def fetch_events_paginated(self, params: Optional[dict] = None, **kwargs) -> PaginatedEventsResult: + try: + args = [] + if kwargs: + params = {**(params or {}), **kwargs} + if params is not None: + args.append(_convert_params_to_camel(params)) + body: dict = {"args": args} + creds = self._get_credentials_dict() + if creds: + body["credentials"] = creds + url = f"{self._resolve_sidecar_host()}/api/{self.exchange_name}/fetchEventsPaginated" + headers = {"Content-Type": "application/json", "Accept": "application/json"} + headers.update(self._get_auth_headers()) + response = self._fetch_with_retry( + lambda: self._api_client.call_api(method="POST", url=url, body=body, header_params=headers) + ) + response.read() + data = self._handle_response(json.loads(response.data)) + return PaginatedEventsResult( + data=[_convert_event(e) for e in data.get("data", [])], + total=data.get("total"), + next_cursor=data.get("nextCursor"), + ) + except ApiException as e: + raise self._parse_api_exception(e) from None + def fetch_events(self, params: Optional[dict] = None, **kwargs) -> List[UnifiedEvent]: try: args = [] diff --git a/sdks/typescript/pmxt/client.ts b/sdks/typescript/pmxt/client.ts index dd4034b3..0c19edc9 100644 --- a/sdks/typescript/pmxt/client.ts +++ b/sdks/typescript/pmxt/client.ts @@ -963,6 +963,36 @@ export abstract class Exchange { } } + async fetchEventsPaginated(params?: any): Promise { + await this.initPromise; + try { + const args: any[] = []; + if (params !== undefined) args.push(params); + const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchEventsPaginated`, { + 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); + } + const json = await response.json(); + const data = this.handleResponse(json); + return { + data: (data.data || []).map(convertEvent), + total: data.total, + nextCursor: data.nextCursor, + }; + } catch (error) { + if (error instanceof PmxtError) throw error; + throw new PmxtError(`Failed to fetchEventsPaginated: ${error}`); + } + } + async fetchEvents(params?: EventFetchParams): Promise { await this.initPromise; try {