pax-api 2.0.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pax_api-2.0.0/src/pax_api.egg-info → pax_api-2.2.0}/PKG-INFO +1 -1
- {pax_api-2.0.0 → pax_api-2.2.0}/pyproject.toml +1 -1
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api/__init__.py +1 -1
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api/client.py +97 -1
- {pax_api-2.0.0 → pax_api-2.2.0/src/pax_api.egg-info}/PKG-INFO +1 -1
- {pax_api-2.0.0 → pax_api-2.2.0}/LICENSE +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/README.md +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/setup.cfg +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api/auth.py +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api/errors.py +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api/py.typed +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api/retry.py +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api/ws_client.py +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api.egg-info/SOURCES.txt +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api.egg-info/dependency_links.txt +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api.egg-info/requires.txt +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/src/pax_api.egg-info/top_level.txt +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/tests/test_auth.py +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/tests/test_client.py +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/tests/test_errors.py +0 -0
- {pax_api-2.0.0 → pax_api-2.2.0}/tests/test_retry.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pax-api"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.0"
|
|
8
8
|
description = "Official Python SDK for the PredictAsiaX Trader Track API — Web3-native prediction markets"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -26,7 +26,7 @@ caps), not a separate host. Same prefix, same URL — promote to production by
|
|
|
26
26
|
upgrading the key's tier.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
__version__ = "2.
|
|
29
|
+
__version__ = "2.2.0"
|
|
30
30
|
|
|
31
31
|
from pax_api.client import PaxClient # noqa: E402
|
|
32
32
|
from pax_api.ws_client import PaxWSClient # noqa: E402
|
|
@@ -25,7 +25,7 @@ from pax_api.auth import sign_request
|
|
|
25
25
|
from pax_api.errors import PaxError, build_error
|
|
26
26
|
from pax_api.retry import run_with_retry
|
|
27
27
|
|
|
28
|
-
__version__ = "2.
|
|
28
|
+
__version__ = "2.2.0" # keep in sync with __init__.py + pyproject.toml
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
# v2: sandbox is a tier on the key, not a separate host. All env aliases
|
|
@@ -285,6 +285,59 @@ class PaxClient:
|
|
|
285
285
|
"""Cancel an open order."""
|
|
286
286
|
return self._request("DELETE", f"/v1/orders/{order_id}")
|
|
287
287
|
|
|
288
|
+
def place_orders_batch(self, orders: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
289
|
+
"""POST /v1/orders/batch — place up to 25 orders in one request (v2.1+).
|
|
290
|
+
|
|
291
|
+
Each order dict uses server field names (market_id / outcome_id / side /
|
|
292
|
+
order_type / size / price / client_order_id). Response contains a
|
|
293
|
+
``results`` array with one entry per input plus a ``summary`` — one
|
|
294
|
+
bad order does not fail the whole batch, inspect ``results[i].ok``.
|
|
295
|
+
Batch cap is env-tunable server-side (BATCH_ORDERS_MAX default 25);
|
|
296
|
+
exceeding returns ``413 BATCH_TOO_LARGE``.
|
|
297
|
+
"""
|
|
298
|
+
return self._request("POST", "/v1/orders/batch", json_body={"orders": orders})
|
|
299
|
+
|
|
300
|
+
def cancel_orders_batch(self, order_ids: List[str]) -> Dict[str, Any]:
|
|
301
|
+
"""DELETE /v1/orders/batch — cancel up to 50 orders in one request (v2.1+).
|
|
302
|
+
|
|
303
|
+
Response mirrors ``place_orders_batch`` shape. Batch cap env-tunable
|
|
304
|
+
(BATCH_CANCEL_MAX default 50).
|
|
305
|
+
"""
|
|
306
|
+
return self._request("DELETE", "/v1/orders/batch", json_body={"orderIds": order_ids})
|
|
307
|
+
|
|
308
|
+
def estimate_fees(self, *, size: str, price: str,
|
|
309
|
+
market_id: Optional[str] = None,
|
|
310
|
+
side: Optional[str] = None,
|
|
311
|
+
action: Optional[str] = None,
|
|
312
|
+
vip_discount: Optional[float] = None,
|
|
313
|
+
sharp_multiplier: Optional[float] = None,
|
|
314
|
+
is_mint_taker: bool = False,
|
|
315
|
+
is_amm_maker: bool = False,
|
|
316
|
+
acquisition_builder_id: Optional[str] = None,
|
|
317
|
+
execution_builder_id: Optional[str] = None,
|
|
318
|
+
operator_id: Optional[str] = None,
|
|
319
|
+
market_creator_id: Optional[str] = None) -> Dict[str, Any]:
|
|
320
|
+
"""GET /v1/fees/estimate — pre-trade 5-actor split preview (v2.1+).
|
|
321
|
+
|
|
322
|
+
Returns the same ``fee`` block shape as post-trade responses (splits +
|
|
323
|
+
attribution + breakdown + ledger_preview). Powered server-side by
|
|
324
|
+
``lib/fiveActorFees`` — env-tunable bps flow through automatically.
|
|
325
|
+
Polymarket has no equivalent endpoint.
|
|
326
|
+
"""
|
|
327
|
+
params: Dict[str, Any] = {"size": str(size), "price": str(price)}
|
|
328
|
+
if market_id is not None: params["market_id"] = market_id
|
|
329
|
+
if side is not None: params["side"] = side
|
|
330
|
+
if action is not None: params["action"] = action
|
|
331
|
+
if vip_discount is not None: params["vip_discount"] = str(vip_discount)
|
|
332
|
+
if sharp_multiplier is not None: params["sharp_multiplier"] = str(sharp_multiplier)
|
|
333
|
+
if is_mint_taker: params["is_mint_taker"] = "1"
|
|
334
|
+
if is_amm_maker: params["is_amm_maker"] = "1"
|
|
335
|
+
if acquisition_builder_id is not None: params["acquisition_builder_id"] = acquisition_builder_id
|
|
336
|
+
if execution_builder_id is not None: params["execution_builder_id"] = execution_builder_id
|
|
337
|
+
if operator_id is not None: params["operator_id"] = operator_id
|
|
338
|
+
if market_creator_id is not None: params["market_creator_id"] = market_creator_id
|
|
339
|
+
return self._request("GET", "/v1/fees/estimate", params=params)
|
|
340
|
+
|
|
288
341
|
def create_market(self, template_id: str, params: Dict[str, Any],
|
|
289
342
|
creator_metadata: Optional[Dict[str, Any]] = None,
|
|
290
343
|
idempotency_key: Optional[str] = None) -> Dict[str, Any]:
|
|
@@ -334,6 +387,49 @@ class PaxClient:
|
|
|
334
387
|
if description is not None: body["description"] = description
|
|
335
388
|
return self._request("POST", "/v1/apply", json_body=body)
|
|
336
389
|
|
|
390
|
+
# ── Public audit verifier (v2.2+, no-auth read-only) ────────────────────
|
|
391
|
+
def audit_status(self) -> Dict[str, Any]:
|
|
392
|
+
"""GET /v1/audit/status — event/batch counts + latest anchor lag."""
|
|
393
|
+
return self._request("GET", "/v1/audit/status")
|
|
394
|
+
|
|
395
|
+
def audit_latest_batch(self) -> Dict[str, Any]:
|
|
396
|
+
"""GET /v1/audit/batches/latest — most recent Merkle batch metadata."""
|
|
397
|
+
return self._request("GET", "/v1/audit/batches/latest")
|
|
398
|
+
|
|
399
|
+
def audit_batch(self, batch_num: int) -> Dict[str, Any]:
|
|
400
|
+
"""GET /v1/audit/batches/{num} — inspect any batch by batch_num."""
|
|
401
|
+
return self._request("GET", f"/v1/audit/batches/{int(batch_num)}")
|
|
402
|
+
|
|
403
|
+
def audit_event(self, seq: int) -> Dict[str, Any]:
|
|
404
|
+
"""GET /v1/audit/events/{seq} — inspect a single hash-chained event."""
|
|
405
|
+
return self._request("GET", f"/v1/audit/events/{int(seq)}")
|
|
406
|
+
|
|
407
|
+
def audit_proof(self, seq: int) -> Dict[str, Any]:
|
|
408
|
+
"""GET /v1/audit/proof/{seq} — Merkle inclusion proof for an event.
|
|
409
|
+
|
|
410
|
+
Returns ``merkle_proof`` (sibling hashes) that proves the event is in
|
|
411
|
+
its batch's ``merkle_root``. Use ``verify_merkle_proof()`` to verify.
|
|
412
|
+
"""
|
|
413
|
+
return self._request("GET", f"/v1/audit/proof/{int(seq)}")
|
|
414
|
+
|
|
415
|
+
def audit_anchor(self, batch_num: int) -> Dict[str, Any]:
|
|
416
|
+
"""GET /v1/audit/anchor/{num} — resolve batch → external anchor URL."""
|
|
417
|
+
return self._request("GET", f"/v1/audit/anchor/{int(batch_num)}")
|
|
418
|
+
|
|
419
|
+
@staticmethod
|
|
420
|
+
def verify_merkle_proof(leaf: str, root: str, proof: List[str]) -> bool:
|
|
421
|
+
"""Verify a Merkle inclusion proof locally — no PAX call needed.
|
|
422
|
+
|
|
423
|
+
Mirrors OpenZeppelin sorted-pair sha256 (identical to server-side
|
|
424
|
+
batcher + on-chain Solidity ``MerkleProof.verify``).
|
|
425
|
+
"""
|
|
426
|
+
import hashlib
|
|
427
|
+
h = leaf
|
|
428
|
+
for sibling in proof:
|
|
429
|
+
a, b = (h, sibling) if h < sibling else (sibling, h)
|
|
430
|
+
h = hashlib.sha256((a + b).encode("ascii")).hexdigest()
|
|
431
|
+
return h == root
|
|
432
|
+
|
|
337
433
|
def get_application(self, application_code: str) -> Dict[str, Any]:
|
|
338
434
|
"""GET /v1/apply/{code} — check application review status."""
|
|
339
435
|
return self._request("GET", f"/v1/apply/{application_code}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|