This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
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 is the default GIT branch.
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:
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):
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 testCI (.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.
Everything lives under the x10/ package:
x10/config.py—TESTNET_CONFIG/MAINNET_CONFIG(ClientConfigfrozen dataclasses bundling endpoints, signing domain, defaults). Entry point for choosing an environment;get_config_by_name("TESTNET"|"MAINNET").x10/core/—ClientConfigdataclasses,EnvConfig(readsX10_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 extendsmodules/base_module.py:BaseModule, which owns the lazily-created sharedaiohttp.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, yieldsWrappedStreamResponseModel[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 extendmodels/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 throughsigning/order_object.py:create_order_object, which hashes via the Starknet domain fromClientConfig.signingand signs withStarkPerpetualAccount.x10/tools/mcp/— experimental MCP server (optionalmcpextra;x10-mcpscript entry point).x10/utils/http.py—WrappedApiResponseModel[T](envelope every REST response is parsed into),get_urlhelper 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).
pytestwithpytest-asyncio— async tests are marked@pytest.mark.asyncio.- REST client tests spin up a local fake API with the
aiohttp_serverfixture, then point the client at it viadataclasses.replace(TESTNET_CONFIG, endpoints=...). No network calls or mocking of the HTTP layer itself. - Assertions use PyHamcrest (
assert_that,equal_to,has_length), not bareassert. - Shared fixtures live in
tests/conftest.pyand delegate to factory functions intests/fixtures/(accounts, markets, orderbook/stream messages). Reuse these when testing anything market- or account-related. freezegunis available for time-sensitive signing tests (nonces, expirations, deterministic hashes intests/signing/).- Tox enforces 65% coverage and runs tests
--forked(process isolation).
- Models: extend
X10BaseModel. It is frozen (ConfigDict(frozen=True)) and auto-generates camelCase aliases for every snake_case field via__init_subclass__(AliasChoicesaccepts both snake and camel on validation; serialization is camelCase). Useto_api_request_json()when building request payloads — notmodel_dump()directly. Wrap hex-encoded ints with theHexValueannotated type. - Money/quantities are always
Decimal, never float. - Clients/modules use name-mangled private attributes (
__config,__session) with public@propertyaccessors, and supportasync 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.
- Per CONTRIBUTING.md, the project doesn't accept PRs larger than ~500 LOC; keep changes scoped.