From fc233a961ca9da698123ea1bd580a95320b3e881 Mon Sep 17 00:00:00 2001 From: alex101xela Date: Thu, 2 Jul 2026 16:43:57 +0400 Subject: [PATCH 1/4] Add CLAUDE.md file --- CLAUDE.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f753899 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,92 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Python SDK (`x10-python-trading-starknet`) for the Extended exchange API — a perpetuals DEX on Starknet. +Async-first (aiohttp/websockets), Pydantic v2 models, Python ≥3.10. Stark signing/hashing is delegated +to the `fast_stark_crypto` Rust wrapper. Published to PyPI via Poetry. + +`starknet` branch is the default branch, + +## Commands + +Dependency management is via Poetry (`poetry install`). All checks run through the Makefile: + +```shell +make format # isort (black profile) + black, line length 120, target py310 +make lint # black --check, flake8, mypy (examples/tests/x10) +make test # tox: poetry install + pytest with coverage gate (--cov-fail-under=65, --forked) +``` + +Run tests directly (faster than tox): + +```shell +poetry run pytest --forked tests/ --import-mode importlib +poetry run pytest --forked tests/clients/test_rest_api_client.py --import-mode importlib # single file +poetry run pytest --forked tests/ -k test_get_markets --import-mode importlib # single test +``` + +CI (`.github/workflows/code-checks.yml`) runs lint + tests on Python 3.10–3.13 for PRs targeting the `starknet` branch. + +## Architecture + +Everything lives under the `x10/` package: + +- **`x10/config.py`** — `TESTNET_CONFIG` / `MAINNET_CONFIG` (`ClientConfig` frozen dataclasses bundling endpoints, + signing domain, defaults). Entry point for choosing an environment; `get_config_by_name("TESTNET"|"MAINNET")`. +- **`x10/core/`** — `ClientConfig` dataclasses, `EnvConfig` (reads `X10_` prefixed env vars), + `StarkPerpetualAccount` (holds Stark keys + API key, signs message hashes), amount/decimal helpers. +- **`x10/clients/`** — one sub-package per client type: + - `blocking/` — `BlockingTradingClient`, synchronous-style order placement built on REST + stream, + waits for order status via account stream. + - `onboarding/` — `OnboardingClient`; derives Stark keys from an Ethereum account via an EIP-712 + sign-message callback (it takes a callback, not a raw L1 private key for security). + - `rest/` — `RestApiClient`, composed of feature modules (`info`, `account`, `orders`, `vault`, `testnet`, `builder`) + exposed as properties. Each module extends `modules/base_module.py:BaseModule`, which owns + the lazily-created shared `aiohttp.ClientSession`, config, and credential access. New REST endpoints + go into the appropriate module (or a new module registered on the client). + - `stream/` — `StreamClient`, one WebSocket connection per topic subscription, + yields `WrappedStreamResponseModel[T]`. + - `streamrpc/` — JSON-RPC-2.0-over-WebSocket client which supports multiple topics over a single connection + (with auto-reconnect and resubscription on disconnect). +- **`x10/models/`** — Pydantic models. All extend `models/base.py:X10BaseModel` + (frozen, camelCase wire aliases — see conventions below). +- **`x10/signing/`** — builds and Stark-signs settlement objects (orders, TP/SL, transfers, withdrawals, onboarding payloads). + Order creation flows through `signing/order_object.py:create_order_object`, which hashes + via the Starknet domain from `ClientConfig.signing` and signs with `StarkPerpetualAccount`. +- **`x10/tools/mcp/`** — experimental MCP server (optional `mcp` extra; `x10-mcp` script entry point). +- **`x10/utils/http.py`** — `WrappedApiResponseModel[T]` (envelope every REST response is parsed into), + `get_url` helper for building URLs. + +**`specs/`** — holds OpenAPI specs for the API. + +**`examples/cases/`** — holds runnable examples (each loads `.env` via `EnvConfig.parse()` and follows the pattern shown in [README.md](./README.md)). + +## Testing + +- `pytest` with `pytest-asyncio` — async tests are marked `@pytest.mark.asyncio`. +- REST client tests spin up a local fake API with the `aiohttp_server` fixture, + then point the client at it via `dataclasses.replace(TESTNET_CONFIG, endpoints=...)`. + No network calls or mocking of the HTTP layer itself. +- Assertions use PyHamcrest (`assert_that`, `equal_to`, `has_length`), not bare `assert`. +- Shared fixtures live in `tests/conftest.py` and delegate to factory functions in `tests/fixtures/` + (accounts, markets, orderbook/stream messages). Reuse these when testing anything market- or account-related. +- `freezegun` is available for time-sensitive signing tests (nonces, expirations, deterministic hashes in `tests/signing/`). +- Tox enforces 65% coverage and runs tests `--forked` (process isolation). + +## Conventions + +- **Models**: extend `X10BaseModel`. It is frozen (`ConfigDict(frozen=True)`) and auto-generates + camelCase aliases for every snake_case field via `__init_subclass__` (`AliasChoices` accepts both snake and + camel on validation; serialization is camelCase). Use `to_api_request_json()` when building + request payloads — not `model_dump()` directly. Wrap hex-encoded ints with the `HexValue` annotated type. +- **Money/quantities are always `Decimal`**, never float. +- **Clients/modules** use name-mangled private attributes (`__config`, `__session`) with public `@property` + accessors, and support `async with` (`close()`/`__aexit__` closes the aiohttp session). +- **Errors**: raise SDK types from `x10/errors.py` (`ValidationError`, `SdkError`, `NotSupportedError`, stream RPC errors) rather than builtins. +- Formatting: black + isort (black profile), line length 120; flake8 with bugbear; mypy runs with the pydantic plugin + and strict init settings — keep new code fully typed. +- Breaking changes to the public API must be documented in [MIGRATION.md](./MIGRATION.md). +- Per [CONTRIBUTING.md](./CONTRIBUTING.md), the project doesn't accept PRs larger than ~500 LOC; keep changes scoped. From 48671f0c35d08acee39072525dfdeded1e1f558c Mon Sep 17 00:00:00 2001 From: alex101xela Date: Thu, 2 Jul 2026 16:50:16 +0400 Subject: [PATCH 2/4] Add CLAUDE.md file --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index f753899..f003d0a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ Python SDK (`x10-python-trading-starknet`) for the Extended exchange API — a p Async-first (aiohttp/websockets), Pydantic v2 models, Python ≥3.10. Stark signing/hashing is delegated to the `fast_stark_crypto` Rust wrapper. Published to PyPI via Poetry. -`starknet` branch is the default branch, +`starknet` is the default GIT branch. ## Commands From 3f6943c4d637988390ff83a653be8895e29cbb52 Mon Sep 17 00:00:00 2001 From: alex101xela Date: Thu, 2 Jul 2026 16:51:08 +0400 Subject: [PATCH 3/4] Add CLAUDE.md file --- CLAUDE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index f003d0a..31c1139 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,6 +30,8 @@ poetry run pytest --forked tests/ -k test_get_markets --import-mode importlib CI (`.github/workflows/code-checks.yml`) runs lint + tests on Python 3.10–3.13 for PRs targeting the `starknet` branch. +All checks and tests are expected to pass. + ## Architecture Everything lives under the `x10/` package: From d00c53440bacd7772375e6bf5034453d884d90f4 Mon Sep 17 00:00:00 2001 From: alex101xela Date: Thu, 2 Jul 2026 16:55:22 +0400 Subject: [PATCH 4/4] Add CLAUDE.md file --- CLAUDE.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 31c1139..b5ac683 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,7 +12,8 @@ to the `fast_stark_crypto` Rust wrapper. Published to PyPI via Poetry. ## Commands -Dependency management is via Poetry (`poetry install`). All checks run through the Makefile: +Dependency management is via Poetry (`poetry install -E mcp` — the `mcp` extra is needed for mypy to pass +on `x10/tools/mcp`; CI installs it too). All checks run through the Makefile: ```shell make format # isort (black profile) + black, line length 120, target py310 @@ -30,6 +31,8 @@ poetry run pytest --forked tests/ -k test_get_markets --import-mode importlib CI (`.github/workflows/code-checks.yml`) runs lint + tests on Python 3.10–3.13 for PRs targeting the `starknet` branch. +Publishing to PyPI happens via GitHub release (`.github/workflows/build-release.yml`). + All checks and tests are expected to pass. ## Architecture