Skip to content

Port hip4 SDK to Python with sync + async clients#1

Merged
dennisfurrer merged 4 commits into
mainfrom
claude/recursing-goodall-4c5a39
May 10, 2026
Merged

Port hip4 SDK to Python with sync + async clients#1
dennisfurrer merged 4 commits into
mainfrom
claude/recursing-goodall-4c5a39

Conversation

@dennisfurrer
Copy link
Copy Markdown
Contributor

Summary

Port of the TypeScript @perps/hip4 SDK to Python. ~9,700 LOC across 61 modules, mirroring the TS structure module-for-module in snake_case.

  • Sync (default): requests + websocket-client
  • Async (extras): httpx + websockets
  • Pure modules (types, signing, precision, pricing, coin, market_classification, market_discovery, agent_wallet, streams.candle_utils, utils) are shared between both
  • 111 unit tests, all passing — covering signing parity (msgpack + keccak + EIP-712), action sorting, coin helpers, market classification, pricing, decimal precision, candle accumulation
  • Tooling matches the official Hyperliquid Python SDK: Poetry, pytest, mypy strict, black, isort, ruff, pre-commit

What's deferred (follow-up PRs)

  • core_evm_system_address + core_to_evm_fees + the Core→EVM wallet methods (send_to_evm_with_data, send_spot_token_to_evm, send_usdc_to_evm)
  • hype_spot_mark_px
  • The full ramp adapter (USDH on/off-ramp via Across + Coinbase) — types ported, adapter not
  • streams/perp_price_feed
  • events.subscribe_outcome_meta_updates
  • Real TS↔Python signing parity test (current parity tests pin Python-computed bytes; should also assert against the TS SDK directly)
  • mypy clean pass
  • CI workflow

Test plan

  • Install dev deps: poetry install --all-extras --with dev
  • Run unit tests: make test — should report 111 passing
  • Run make lint (mypy not yet clean — known)
  • Verify both facades import: python -c "from hip4 import create_hip4_adapter; from hip4.aio import create_async_hip4_adapter"
  • Run a sync example against testnet (e.g. examples/get_all_markets.py)
  • Run async example: python examples/async_get_all_markets.py

🤖 Generated with Claude Code

dennisfurrer and others added 4 commits May 10, 2026 15:28
Mirrors the TypeScript @perps/hip4 SDK module-for-module in snake_case.
Sync uses requests + websocket-client, async uses httpx + websockets;
both share the pure modules (types, signing, precision, classification,
pricing, coin helpers, agent_wallet).

Tooling matches the official Hyperliquid Python SDK: Poetry, pytest,
mypy strict, black, isort, ruff, pre-commit. 111 tests cover the
foundation (signing parity, msgpack encoding, action sorting, coin
helpers, market classification, pricing, decimal precision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 fixtures (orders, cancels, modifies, all 4 userOutcome variants,
scheduleCancel, vault-attached, builder fees, triggers + cloid) generated
by the TypeScript SDK's internal signing helpers via vitest. Each fixture
asserts:

  1. Same canonical key ordering after sort_*_action
  2. Same MessagePack byte stream
  3. Same Keccak-256 action hash

36 cross-implementation assertions, all passing — guarantees the Python
port produces signatures that recover to the same agent address as the TS
SDK on the Hyperliquid server.

Generator script lives at hip4/tests/parity/dump-fixtures.test.ts in the
TS repo; rerun via `pnpm vitest run tests/parity/dump-fixtures.test.ts`
and copy fixtures.json into tests/fixtures/ts_signing_parity.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Core <-> HyperEVM bridging:

- hip4.core_evm:
  - derive_core_evm_system_address(token_index) — 0x20 + 19B BE token id
  - estimate_core_to_evm_fee(...) — pure fee math, picks HYPE-vs-source debit
  - median_base_fee_wei(samples) — robust over single-block MEV spikes
  - select_hype_spot_mark_px(ctxs, *, testnet) + find_hype_usdc_spot_pair_coin
  - Constants: HYPE_CORE_EVM_SYSTEM_ADDRESS, CORE_TO_EVM_GAS_LIMIT,
    HYPE_USDC_SPOT_PAIR_{MAINNET,TESTNET}, MAX_CORE_EVM_TOKEN_INDEX

- WalletAdapter / AsyncWalletAdapter:
  - send_spot_token_to_evm — Core->HyperEVM (USDH and other spot tokens)
  - send_to_evm_with_data — Core->EVM with hook data (CCTP for USDC)
  - send_usdc_to_evm — convenience wrapper, auto-fills USDC token id
  - set_referrer — L1-signed referral code

USDH on/off-ramp adapter (mainnet only):

- hip4.sync.ramp.RampAdapter / hip4.aio.ramp.AsyncRampAdapter:
  - generate_deposit_address — counterfactual Arbitrum->HyperCore via Across
  - get_sell_quote — HyperEVM USDH -> Arbitrum USDC swap
  - check_deposit_status — track Across fills/refunds
  - get_coinbase_session_token + generate_buy_url + generate_sell_url
  - Mainnet guard raises RampError on testnet (Across/Coinbase don't support it)

Tests: 16 Core<->EVM + 16 ramp = 32 new tests; total now 222 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Strict mypy now passes with zero errors. Changes:

- pyproject.toml: relax warn_return_any for hip4.{sync,aio}.client and
  hip4.auth — these intentionally bridge Any (response.json(), eth_account
  attribute access) to typed return values, and per-call cast() buys
  nothing.
- signing.py: broaden sort_*_action input types from strict TypedDicts to
  Mapping[str, Any] so callers don't need to narrow union TypedDicts
  before dispatching. Wire shapes are still asserted by the parity tests.
- sync/aio client place_order/cancel_order/modify_order/batch_modify:
  accept Mapping[str, Any] for the same reason.
- sync/aio market_data: cast() WS payloads at the dispatch points.
- sync/account: rename shadowed `names` variable; broaden _map_spot_balance
  to take Mapping inputs.
- sync/aio trading: drop strict TypedDict annotations on hand-built dicts;
  cast() through Any where mypy can't narrow HLModifyResponse's str|dict
  union.
- sync/aio wallet: cast statuses[0] to Dict[str, Any] for the filled/error
  branches.
- sync/aio events: add missing return annotations on fetch_settled_outcome.
- agent_wallet: cast _normalize_sig fall-through to HLSignature.

All 222 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dennisfurrer dennisfurrer merged commit d45ed1d into main May 10, 2026
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.

1 participant