From dd0d76f9691666679bbe0682ea513ef611621af2 Mon Sep 17 00:00:00 2001 From: Lethale <175728510+lethaale@users.noreply.github.com> Date: Fri, 19 Dec 2025 11:07:21 +0100 Subject: [PATCH] README testnet link + formatting --- README.md | 90 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 2a0a1ef..3672e12 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Python client for [Extended API](https://api.docs.extended.exchange/). Minimum Python version required to use this library is `3.10` (you can use [pyenv](https://github.com/pyenv/pyenv) to manage your Python versions easily). -## Installation +## Installation ```shell pip install x10-python-trading-starknet @@ -12,29 +12,28 @@ pip install x10-python-trading-starknet Our SDK makes use of a [Rust Library](https://github.com/x10xchange/stark-crypto-wrapper) to accelerate signing and hashing of stark components. Currently this library supports the following environments -| | 3.9 | 3.10 | 3.11 | 3.12 | -| --------------------- | :---: | :---: | :---: | :---: | -| linux (glibc) - x86 | ✅ | ✅ | ✅ | ✅ | -| linux (musl) - x86 | ✅ | ✅ | ✅ | ✅ | -| linux (glibc) - arm64 | ✅ | ✅ | ✅ | ✅ | -| linux (musl) - arm64 | ✅ | ✅ | ✅ | ✅ | -| OSX - arm64 | ✅ | ✅ | ✅ | ✅ | -| windows - x86 | ✅ | ✅ | ✅ | ✅ | -| windows - arm64 | ⚠️ | ⚠️ | ⚠️ | ⚠️ | - - +| | 3.9 | 3.10 | 3.11 | 3.12 | +| --------------------- | :-: | :--: | :--: | :--: | +| linux (glibc) - x86 | ✅ | ✅ | ✅ | ✅ | +| linux (musl) - x86 | ✅ | ✅ | ✅ | ✅ | +| linux (glibc) - arm64 | ✅ | ✅ | ✅ | ✅ | +| linux (musl) - arm64 | ✅ | ✅ | ✅ | ✅ | +| OSX - arm64 | ✅ | ✅ | ✅ | ✅ | +| windows - x86 | ✅ | ✅ | ✅ | ✅ | +| windows - arm64 | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ## TLDR: -Register at [Extended Testnet](https://testnet.extended.exchange/) by connecting a supported Ethereum Wallet. +Register at [Extended Testnet](https://starknet.sepolia.extended.exchange/perp) by connecting a supported Ethereum Wallet. + +Navigate to [Api Management](https://starknet.sepolia.extended.exchange/api-management) -Navigate to [Api Management](https://testnet.extended.exchange/api-management) 1. Generate an API key 2. Show API details (You will need these details to initialise a trading client) Instantiate a Trading Account -```python +```python from x10.perpetual.accounts import StarkPerpetualAccount api_key:str = "" #from api-management public_key:str = "" #from api-management @@ -50,6 +49,7 @@ stark_account = StarkPerpetualAccount( ``` Instantiate a Trading Client + ```python from x10.perpetual.accounts import StarkPerpetualAccount from x10.perpetual.configuration import TESTNET_CONFIG @@ -75,15 +75,20 @@ There is also a skeleton implementation of a [blocking client](examples/simple_c The SDK currently provides functionality across three main modules -### Order Management Module +### Order Management Module + The order module is accessed using the `orders` property of the trading client + ```python trading_client.orders ``` + TODO -### Account Module +### Account Module + The account module is accessed using the account property of the trading client + ```python trading_client.account ``` @@ -91,6 +96,7 @@ trading_client.account it exposes functionality related to managing an active trading account #### `get_balance` + Fetches the balance of the user's account. ```python @@ -100,6 +106,7 @@ Fetches the balance of the user's account. ``` #### `get_positions` + Fetches the current positions of the user's account. It can filter the positions based on market names and position side. ```python @@ -109,6 +116,7 @@ Fetches the current positions of the user's account. It can filter the positions ``` returns a list of + ```python class PositionModel(X10BaseModel): id: int @@ -131,6 +139,7 @@ class PositionModel(X10BaseModel): ``` #### `get_positions_history` + Fetches the historical positions of the user's account. It can filter the positions based on market names and position side. ```python @@ -139,7 +148,8 @@ Fetches the historical positions of the user's account. It can filter the positi logger.info("Positions: %s", positions.to_pretty_json()) ``` -returns a list of +returns a list of + ```python class PositionHistoryModel(X10BaseModel): id: int @@ -157,14 +167,17 @@ class PositionHistoryModel(X10BaseModel): ``` #### `get_open_orders` + Fetches the open orders of the user's account. It can filter the orders based on market names, order type, and order side. ```python open_orders = await trading_client.account.get_open_orders() await trading_client.orders.mass_cancel(order_ids=[order.id for order in open_orders.data]) ``` + returns a list of -```python + +```python class OpenOrderModel(X10BaseModel): id: int account_id: int @@ -186,6 +199,7 @@ class OpenOrderModel(X10BaseModel): ``` #### `get_orders_history` + Fetches the historical orders of the user's account. It can filter the orders based on market names, order type, and order side ```python @@ -202,15 +216,19 @@ Fetches the historical orders of the user's account. It can filter the orders ba order_side=OrderSide.BUY ) ``` + returns a list of `OpenOrderModel` #### `get_trades` + Fetches the trades of the user's account. It can filter the trades based on market names, trade side, and trade type. ```python + ``` #### `get_fees` + Fetches the trading fees for the specified markets. ```python @@ -218,6 +236,7 @@ pass ``` #### `get_leverage` + Fetches the leverage for the specified markets. ```python @@ -226,6 +245,7 @@ Fetches the leverage for the specified markets. ``` returns a list of + ```python class AccountLeverage(X10BaseModel): market: str @@ -233,17 +253,21 @@ class AccountLeverage(X10BaseModel): ``` #### `update_leverage` + Updates the leverage for a specific market. ```python await trading_client.account.update_leverage(market_name="BTC-USD", leverage=Decimal("20.0")) ``` -### Markets Info Module +### Markets Info Module + The markets module is accessed using the `markets_info` property of the trading client + ```python trading_client.markets_info ``` + TODO ## SDK Environment configurations (Since version 0.3.0) @@ -251,32 +275,39 @@ TODO The SDK is controlled by an `EndpointConfiguration` object passed to the various methods and clients, several helpful instances are defined in [configuration.py](x10/perpetual/configuration.py) ### `MAINNET_CONFIG` vs `MAINNET_CONFIG_LEGACY_SIGNING_DOMAIN` -If you previously onboarded to our mainnet environment on `app.x10.exchange`, you should use the `MAINNET_CONFIG_LEGACY_SIGNING_DOMAIN` configuration bundle, as this will allow you to regenerate the same l2 keys as were created by our mainnet environment that was running on a legacy domain. + +If you previously onboarded to our mainnet environment on `app.x10.exchange`, you should use the `MAINNET_CONFIG_LEGACY_SIGNING_DOMAIN` configuration bundle, as this will allow you to regenerate the same l2 keys as were created by our mainnet environment that was running on a legacy domain. All new accounts should use the `MAINNET_CONFIG` configuration bundle. ## OnBoarding via SDK (Since Version 0.3.0) -To onboard to the Extended Exchange, the `UserClient` defined in [user_client.py](x10/perpetual/user_client/user_client.py) provides a way to use an Ethereum account to onboard onto the Extended Exchange. +To onboard to the Extended Exchange, the `UserClient` defined in [user_client.py](x10/perpetual/user_client/user_client.py) provides a way to use an Ethereum account to onboard onto the Extended Exchange. ### TLDR - Check out: [onboarding_example.py](examples/onboarding_example.py) ### `onboard(referral_code: Optional[str] = None) -> OnBoardedAccount` + This method handles the onboarding process of a user. It generates an L2 key pair from the user's L1 Ethereum account, creates an onboarding payload, and sends it to the onboarding endpoint. Upon successful onboarding, it returns an `OnBoardedAccount` object containing the default account and the L2 key pair. ### `onboard_subaccount(account_index: int, description: str | None = None) -> OnBoardedAccount` + This method onboards a subaccount associated with the user's main account. It allows you to specify an `account_index` and an optional description. If a subaccount with the given index already exists, it retrieves and returns that subaccount. Otherwise, it creates a new subaccount and returns an `OnBoardedAccount` object with the subaccount details and the associated L2 key pair. ### `get_accounts() -> List[OnBoardedAccount]` + This method retrieves all the accounts associated with the user. It returns a list of `OnBoardedAccount` objects, each containing the account details and corresponding L2 key pair. ### `create_account_api_key(account: AccountModel, description: str | None) -> str` + This method generates an API key for a specified account. You can provide an optional description for the API key. It returns the newly created API key as a string. ### `perform_l1_withdrawal() -> str` + This method initiates a withdrawal from Layer 2 (L2) to Layer 1 (L1) using the user's Ethereum account. It calls the underlying contract function to perform the withdrawal and returns a string, typically a transaction hash or status. ### `available_l1_withdrawal_balance() -> Decimal` + This method retrieves the available balance for L1 withdrawals. It calls the underlying contract function to fetch the withdrawal balance and returns the balance as a `Decimal` value. ### Process of Obtaining a Stark Key Pair from an Ethereum Account @@ -294,9 +325,10 @@ The first step in the process is to generate a signing structure that will be si ##### a. Define the Signing Structure The message to be signed includes: -1. account index, -2. the Ethereum wallet address, -3. and whether the terms of service (TOS) are accepted. + +1. account index, +2. the Ethereum wallet address, +3. and whether the terms of service (TOS) are accepted. in the function `get_key_derivation_struct_to_sign`, the signing structure is constructed as follows: @@ -352,20 +384,19 @@ Once the signing structure is prepared, it is signed using the Ethereum private The signature obtained from the Ethereum account is then used to derive the Stark private key. This is done by truncating the r value from the Ethereum signature and using it as the basis for the Stark private key: -```python +```python def get_private_key_from_eth_signature(eth_signature: str) -> int: eth_sig_truncated = re.sub("^0x", "", eth_signature) r = eth_sig_truncated[:64] return stark_sign.grind_key(int(r, 16), stark_sign.EC_ORDER) ``` -`stark_sign.grind_key` is a function imported from [`vendor/starkware/crypto/signature/signature.py`](vendor/starkware/crypto/signature/signature.py) +`stark_sign.grind_key` is a function imported from [`vendor/starkware/crypto/signature/signature.py`](vendor/starkware/crypto/signature/signature.py) ## Depositing via SDK (Since Version 0.3.0) There is a new function `deposit` available on the [`AccountModule`](x10/perpetual/trading_client/account_module.py) which provides the ability to directly deposit USDC into your StarkEx account. For more details check out `call_stark_perpetual_deposit` in [contract.py](x10/perpetual/contract.py) - ## Contribution Make sure you have [poetry](https://python-poetry.org/) installed. @@ -378,8 +409,7 @@ Make sure you have [poetry](https://python-poetry.org/) installed. - Run it: `python -m examples.placed_order_example` Custom commands: + - `make format` - format code with `black` - `make lint` - run `safety`, `black`, `flake8` and `mypy` checks - `make test` - run tests - -