Hardening

This commit is contained in:
Daniel Sapkota
2026-07-05 22:15:30 -04:00
parent cb4438655c
commit a18e1ac7fb
17 changed files with 815 additions and 68 deletions
+111 -11
View File
@@ -14,8 +14,10 @@ from __future__ import annotations
import asyncio
import itertools
import time
from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor
from dataclasses import asdict
from typing import Any
from typing import Any, TypeVar
import httpx
@@ -27,6 +29,8 @@ from polymaker.logging import get_logger
log = get_logger("execution.gateway")
_T = TypeVar("_T")
def _tick_str(tick: float) -> str:
return f"{tick:g}"
@@ -55,11 +59,27 @@ class ExecutionGateway:
self._paper_ids = itertools.count(1)
self._hb_id: str = "" # heartbeat chain
self._hb_failures: int = 0
# dedicated, bounded pool for blocking order/HTTP calls so a burst of
# requotes across many markets can't starve the default executor
self._pool = ThreadPoolExecutor(max_workers=8, thread_name_prefix="clob-io")
@property
def paper(self) -> bool:
return self._paper
@property
def order_pressure(self) -> float:
"""0 = plenty of order-post budget, 1 = about to queue (shed load)."""
return self._order_bucket.pressure
def close(self) -> None:
self._pool.shutdown(wait=False, cancel_futures=True)
async def _io(self, fn: Callable[..., _T], *args: Any) -> _T:
"""Run a blocking client call on the dedicated pool."""
loop = asyncio.get_running_loop()
return await loop.run_in_executor(self._pool, fn, *args)
@property
def creds(self) -> Any:
return self._creds
@@ -94,6 +114,9 @@ class ExecutionGateway:
host=self._cfg.wallet.clob_host,
chain_id=self._cfg.wallet.chain_id,
key=sec.pk,
# use_server_time=False: fetching /time before EVERY signed order
# adds a full round-trip per op (latency killer through a proxy).
# We check clock drift once below and rely on the local (NTP) clock.
signature_type=self._cfg.wallet.signature_type,
funder=sec.browser_address,
)
@@ -101,12 +124,28 @@ class ExecutionGateway:
client.set_api_creds(creds)
return client, creds, client.get_address()
self._client, self._creds, self._address = await asyncio.to_thread(_build)
self._client, self._creds, self._address = await self._io(_build)
await self._check_clock_drift()
# funds/positions live on the funder (proxy/deposit wallet); fall back to EOA
self._funder = sec.browser_address or self._address
log.info("gateway_connected", signer=self._address[:10], funder=self._funder[:10],
paper=self._paper)
async def _check_clock_drift(self) -> None:
"""Warn once if the local clock is skewed vs the exchange (affects L2 auth)."""
try:
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.get(f"{self._cfg.wallet.clob_host}/time")
server = float(r.text.strip().strip('"'))
drift = abs(time.time() - server)
if drift > 5.0:
log.warning("clock_drift", drift_s=round(drift, 1),
note="sync system clock (NTP) — large skew can fail order auth")
else:
log.info("clock_ok", drift_s=round(drift, 1))
except (httpx.HTTPError, ValueError) as exc:
log.warning("clock_check_failed", err=str(exc))
# ── placement ───────────────────────────────────────────────────────
async def place(self, quotes: list[Quote], meta: MarketMeta) -> list[OpenOrder]:
if not quotes:
@@ -138,7 +177,7 @@ class ExecutionGateway:
return self._parse_place_response(resp, quotes)
try:
return await asyncio.to_thread(_place)
return await self._io(_place)
except Exception as exc: # noqa: BLE001 - surface + continue; engine handles error rate
log.error("place_failed", err=str(exc), n=len(quotes))
return []
@@ -172,7 +211,7 @@ class ExecutionGateway:
self._client.cancel_orders(order_ids)
try:
await asyncio.to_thread(_cancel)
await self._io(_cancel)
return True
except Exception as exc: # noqa: BLE001
log.error("cancel_failed", err=str(exc), n=len(order_ids))
@@ -189,7 +228,7 @@ class ExecutionGateway:
self._client.cancel_market_orders(OrderMarketCancelParams(asset_id=asset_id))
try:
await asyncio.to_thread(_cancel)
await self._io(_cancel)
return True
except Exception as exc: # noqa: BLE001
log.error("cancel_asset_failed", err=str(exc), token=asset_id[:12])
@@ -198,7 +237,7 @@ class ExecutionGateway:
async def cancel_all(self) -> None:
if self._paper or self._client is None:
return
await asyncio.to_thread(self._client.cancel_all)
await self._io(self._client.cancel_all)
log.info("cancel_all_sent")
# ── market (taker) orders — used by moneydoctor, NOT the maker strategy ──
@@ -232,7 +271,7 @@ class ExecutionGateway:
except Exception as exc: # noqa: BLE001 - surface as data, never crash the caller
return {"status": "failed", "error": str(exc)}
return await asyncio.to_thread(_do)
return await self._io(_do)
async def get_book(self, token_id: str) -> dict[str, float]:
"""Live best bid/ask + touch depth for one token (public REST)."""
@@ -254,6 +293,24 @@ class ExecutionGateway:
log.warning("get_book_failed", err=str(exc))
return {}
async def get_full_book(
self, token_id: str
) -> tuple[list[tuple[float, float]], list[tuple[float, float]], str | None] | None:
"""Full L2 book (bids, asks, hash) via public REST — for periodic
integrity refresh against the WS book."""
try:
async with httpx.AsyncClient(timeout=15.0) as c:
r = await c.get(f"{self._cfg.wallet.clob_host}/book",
params={"token_id": token_id})
r.raise_for_status()
b = r.json()
bids = [(float(x["price"]), float(x["size"])) for x in b.get("bids", [])]
asks = [(float(x["price"]), float(x["size"])) for x in b.get("asks", [])]
return bids, asks, b.get("hash")
except (httpx.HTTPError, KeyError, ValueError) as exc:
log.warning("get_full_book_failed", err=str(exc))
return None
async def token_balance(self, token_id: str) -> float:
"""Exact on-chain conditional-token balance (shares) held by the funder.
@@ -291,11 +348,54 @@ class ExecutionGateway:
return None
try:
return await asyncio.to_thread(_read)
return await self._io(_read)
except Exception as exc: # noqa: BLE001
log.warning("token_balance_failed", err=str(exc))
return None
async def token_balances(self, token_ids: list[str]) -> dict[str, float] | None:
"""Batch on-chain balances for several tokens in one RPC session.
Used by the position-divergence monitor. Returns None on RPC failure.
"""
if not token_ids:
return {}
def _read() -> dict[str, float] | None:
from web3 import Web3
from web3.middleware import ExtraDataToPOAMiddleware
configured = self._cfg.secrets.polygon_rpc or self._cfg.wallet.polygon_rpc
rpcs = [configured, "https://polygon-bor-rpc.publicnode.com",
"https://polygon.llamarpc.com", "https://rpc.ankr.com/polygon"]
abi = [{"name": "balanceOf", "type": "function", "stateMutability": "view",
"inputs": [{"name": "a", "type": "address"}, {"name": "id", "type": "uint256"}],
"outputs": [{"name": "", "type": "uint256"}]}]
funder = None
for rpc in dict.fromkeys(rpcs):
try:
w3 = Web3(Web3.HTTPProvider(rpc, request_kwargs={"timeout": 20}))
w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
ctf = w3.eth.contract(
address=Web3.to_checksum_address("0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"),
abi=abi,
)
funder = Web3.to_checksum_address(self.funder)
out: dict[str, float] = {}
for tid in token_ids:
raw = ctf.functions.balanceOf(funder, int(tid)).call()
out[tid] = float(raw) / 1e6
return out
except Exception: # noqa: BLE001, PERF203
continue
return None
try:
return await self._io(_read)
except Exception as exc: # noqa: BLE001
log.warning("token_balances_failed", err=str(exc))
return None
async def collateral_balance(self) -> float:
"""pUSD balance (float) on the funder."""
ba = await self.balance_allowance()
@@ -324,7 +424,7 @@ class ExecutionGateway:
return self._client.post_heartbeat(self._hb_id)
try:
resp = await asyncio.to_thread(_beat)
resp = await self._io(_beat)
new_id = _first(resp, "heartbeat_id", "heartbeatId", "id")
self._hb_id = str(new_id) if new_id else ""
if self._hb_failures:
@@ -371,7 +471,7 @@ class ExecutionGateway:
return out
try:
return await asyncio.to_thread(_get)
return await self._io(_get)
except Exception as exc: # noqa: BLE001
log.warning("open_orders_failed", err=str(exc))
return []
@@ -411,7 +511,7 @@ class ExecutionGateway:
return result
try:
return await asyncio.to_thread(_get)
return await self._io(_get)
except Exception as exc: # noqa: BLE001
log.warning("balance_allowance_failed", err=str(exc))
return {}