Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 60 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,36 @@ 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
```

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 = "<api>" #from api-management
public_key:str = "<public>" #from api-management
Expand All @@ -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
Expand All @@ -75,22 +75,28 @@ 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
```

it exposes functionality related to managing an active trading account

#### `get_balance`

Fetches the balance of the user's account.

```python
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -202,22 +216,27 @@ 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
pass
```

#### `get_leverage`

Fetches the leverage for the specified markets.

```python
Expand All @@ -226,57 +245,69 @@ Fetches the leverage for the specified markets.
```

returns a list of

```python
class AccountLeverage(X10BaseModel):
market: str
leverage: Decimal
```

#### `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)

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
Expand All @@ -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:

Expand Down Expand Up @@ -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.
Expand All @@ -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