x10-python-trading-starknet 1.3.1__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/PKG-INFO +8 -5
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/README.md +4 -1
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/pyproject.toml +5 -5
- x10_python_trading_starknet-2.0.0/x10/clients/blocking/__init__.py +3 -0
- x10_python_trading_starknet-1.3.1/x10/perpetual/simple_client/simple_trading_client.py → x10_python_trading_starknet-2.0.0/x10/clients/blocking/blocking_trading_client.py +47 -39
- x10_python_trading_starknet-2.0.0/x10/clients/onboarding/__init__.py +1 -0
- x10_python_trading_starknet-2.0.0/x10/clients/onboarding/modules/account_module.py +32 -0
- x10_python_trading_starknet-2.0.0/x10/clients/onboarding/modules/auth_module.py +89 -0
- x10_python_trading_starknet-2.0.0/x10/clients/onboarding/modules/base_module.py +51 -0
- x10_python_trading_starknet-2.0.0/x10/clients/onboarding/onboarding_client.py +33 -0
- x10_python_trading_starknet-2.0.0/x10/clients/rest/__init__.py +1 -0
- {x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client → x10_python_trading_starknet-2.0.0/x10/clients/rest/modules}/account_module.py +87 -72
- {x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client → x10_python_trading_starknet-2.0.0/x10/clients/rest/modules}/base_module.py +18 -14
- x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client/info_markets_module.py → x10_python_trading_starknet-2.0.0/x10/clients/rest/modules/info_module.py +30 -11
- {x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client → x10_python_trading_starknet-2.0.0/x10/clients/rest/modules}/order_management_module.py +8 -15
- {x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client → x10_python_trading_starknet-2.0.0/x10/clients/rest/modules}/testnet_module.py +15 -17
- {x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client → x10_python_trading_starknet-2.0.0/x10/clients/rest/modules}/vault_module.py +26 -43
- x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client/trading_client.py → x10_python_trading_starknet-2.0.0/x10/clients/rest/rest_api_client.py +33 -34
- x10_python_trading_starknet-2.0.0/x10/clients/stream/__init__.py +2 -0
- {x10_python_trading_starknet-1.3.1/x10/perpetual/stream_client → x10_python_trading_starknet-2.0.0/x10/clients/stream}/stream_client.py +20 -22
- x10_python_trading_starknet-1.3.1/x10/perpetual/stream_client/perpetual_stream_connection.py → x10_python_trading_starknet-2.0.0/x10/clients/stream/stream_connection.py +10 -13
- x10_python_trading_starknet-2.0.0/x10/config.py +99 -0
- x10_python_trading_starknet-1.3.1/x10/perpetual/amounts.py → x10_python_trading_starknet-2.0.0/x10/core/amount.py +4 -13
- x10_python_trading_starknet-2.0.0/x10/core/stark_account.py +43 -0
- x10_python_trading_starknet-2.0.0/x10/errors.py +22 -0
- x10_python_trading_starknet-2.0.0/x10/models/__init__.py +0 -0
- x10_python_trading_starknet-2.0.0/x10/models/account.py +40 -0
- x10_python_trading_starknet-1.3.1/x10/perpetual/assets.py → x10_python_trading_starknet-2.0.0/x10/models/asset.py +4 -3
- x10_python_trading_starknet-1.3.1/x10/perpetual/balances.py → x10_python_trading_starknet-2.0.0/x10/models/balance.py +1 -1
- x10_python_trading_starknet-2.0.0/x10/models/bridge.py +17 -0
- x10_python_trading_starknet-1.3.1/x10/perpetual/candles.py → x10_python_trading_starknet-2.0.0/x10/models/candle.py +1 -1
- x10_python_trading_starknet-2.0.0/x10/models/client.py +14 -0
- x10_python_trading_starknet-2.0.0/x10/models/fee.py +10 -0
- x10_python_trading_starknet-1.3.1/x10/perpetual/funding_rates.py → x10_python_trading_starknet-2.0.0/x10/models/funding_rate.py +1 -1
- x10_python_trading_starknet-2.0.0/x10/models/http.py +60 -0
- x10_python_trading_starknet-1.3.1/x10/perpetual/markets.py → x10_python_trading_starknet-2.0.0/x10/models/market.py +4 -4
- x10_python_trading_starknet-1.3.1/x10/perpetual/orders.py → x10_python_trading_starknet-2.0.0/x10/models/order.py +9 -2
- x10_python_trading_starknet-1.3.1/x10/perpetual/orderbooks.py → x10_python_trading_starknet-2.0.0/x10/models/orderbook.py +1 -1
- x10_python_trading_starknet-1.3.1/x10/perpetual/positions.py → x10_python_trading_starknet-2.0.0/x10/models/position.py +1 -1
- x10_python_trading_starknet-2.0.0/x10/models/settings.py +5 -0
- x10_python_trading_starknet-2.0.0/x10/models/testnet.py +5 -0
- x10_python_trading_starknet-1.3.1/x10/perpetual/trades.py → x10_python_trading_starknet-2.0.0/x10/models/trade.py +2 -2
- x10_python_trading_starknet-1.3.1/x10/perpetual/transfers.py → x10_python_trading_starknet-2.0.0/x10/models/transfer.py +4 -5
- x10_python_trading_starknet-2.0.0/x10/models/vault.py +20 -0
- x10_python_trading_starknet-1.3.1/x10/perpetual/withdrawals.py → x10_python_trading_starknet-2.0.0/x10/models/withdrawal.py +6 -6
- x10_python_trading_starknet-2.0.0/x10/py.typed +0 -0
- x10_python_trading_starknet-2.0.0/x10/signing/__init__.py +0 -0
- {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/limit_order_object_settlement.py +11 -11
- {x10_python_trading_starknet-1.3.1/x10/perpetual/user_client → x10_python_trading_starknet-2.0.0/x10/signing}/onboarding.py +56 -27
- {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/order_object.py +32 -30
- {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/order_object_settlement.py +15 -21
- {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/transfer_object.py +18 -24
- {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/withdrawal_object.py +20 -26
- x10_python_trading_starknet-2.0.0/x10/tools/__init__.py +0 -0
- {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/tools}/orderbook.py +27 -28
- x10_python_trading_starknet-2.0.0/x10/utils/__init__.py +0 -0
- x10_python_trading_starknet-2.0.0/x10/utils/date.py +23 -0
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/http.py +39 -82
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/order.py +1 -1
- x10_python_trading_starknet-2.0.0/x10/version.py +3 -0
- x10_python_trading_starknet-1.3.1/x10/config.py +0 -12
- x10_python_trading_starknet-1.3.1/x10/errors.py +0 -2
- x10_python_trading_starknet-1.3.1/x10/perpetual/accounts.py +0 -86
- x10_python_trading_starknet-1.3.1/x10/perpetual/bridges.py +0 -17
- x10_python_trading_starknet-1.3.1/x10/perpetual/clients.py +0 -8
- x10_python_trading_starknet-1.3.1/x10/perpetual/configuration.py +0 -68
- x10_python_trading_starknet-1.3.1/x10/perpetual/fees.py +0 -18
- x10_python_trading_starknet-1.3.1/x10/perpetual/stream_client/__init__.py +0 -3
- x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client/__init__.py +0 -3
- x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client/info_module.py +0 -29
- x10_python_trading_starknet-1.3.1/x10/perpetual/user_client/user_client.py +0 -201
- x10_python_trading_starknet-1.3.1/x10/utils/date.py +0 -13
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/LICENSE +0 -0
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/__init__.py +0 -0
- {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/clients}/__init__.py +0 -0
- {x10_python_trading_starknet-1.3.1/x10/perpetual/user_client → x10_python_trading_starknet-2.0.0/x10/clients/onboarding/modules}/__init__.py +0 -0
- {x10_python_trading_starknet-1.3.1/x10/utils → x10_python_trading_starknet-2.0.0/x10/clients/rest/modules}/__init__.py +0 -0
- /x10_python_trading_starknet-1.3.1/x10/perpetual/user_client/l1_signing.py → /x10_python_trading_starknet-2.0.0/x10/core/__init__.py +0 -0
- /x10_python_trading_starknet-1.3.1/x10/utils/model.py → /x10_python_trading_starknet-2.0.0/x10/models/base.py +0 -0
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/log.py +0 -0
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/nonce.py +0 -0
- {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/string.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: x10-python-trading-starknet
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Python client for X10 API
|
|
5
5
|
Home-page: https://github.com/x10xchange/python_sdk
|
|
6
6
|
Author: X10
|
|
@@ -20,9 +20,9 @@ Requires-Dist: fast-stark-crypto (==0.5.0)
|
|
|
20
20
|
Requires-Dist: pydantic (>=2.9.0)
|
|
21
21
|
Requires-Dist: pyyaml (>=6.0.1)
|
|
22
22
|
Requires-Dist: sortedcontainers (>=2.4.0)
|
|
23
|
-
Requires-Dist: strenum (>=0.4.15
|
|
24
|
-
Requires-Dist: tenacity (>=9.1.2
|
|
25
|
-
Requires-Dist: websockets (>=12.0
|
|
23
|
+
Requires-Dist: strenum (>=0.4.15)
|
|
24
|
+
Requires-Dist: tenacity (>=9.1.2)
|
|
25
|
+
Requires-Dist: websockets (>=12.0)
|
|
26
26
|
Project-URL: Documentation, https://api.docs.extended.exchange/
|
|
27
27
|
Project-URL: Repository, https://github.com/x10xchange/python_sdk
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
@@ -286,7 +286,7 @@ All new accounts should use the `MAINNET_CONFIG` configuration bundle.
|
|
|
286
286
|
|
|
287
287
|
## OnBoarding via SDK (Since Version 0.3.0)
|
|
288
288
|
|
|
289
|
-
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.
|
|
289
|
+
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.
|
|
290
290
|
|
|
291
291
|
### TLDR - Check out: [onboarding_example.py](examples/onboarding_example.py)
|
|
292
292
|
|
|
@@ -394,6 +394,9 @@ def get_private_key_from_eth_signature(eth_signature: str) -> int:
|
|
|
394
394
|
|
|
395
395
|
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)
|
|
396
396
|
|
|
397
|
+
## Breaking changes
|
|
398
|
+
|
|
399
|
+
For a detailed list of breaking changes, please refer to the [MIGRATION.md](MIGRATION.md) file.
|
|
397
400
|
|
|
398
401
|
## Contributing
|
|
399
402
|
|
|
@@ -257,7 +257,7 @@ All new accounts should use the `MAINNET_CONFIG` configuration bundle.
|
|
|
257
257
|
|
|
258
258
|
## OnBoarding via SDK (Since Version 0.3.0)
|
|
259
259
|
|
|
260
|
-
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.
|
|
260
|
+
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.
|
|
261
261
|
|
|
262
262
|
### TLDR - Check out: [onboarding_example.py](examples/onboarding_example.py)
|
|
263
263
|
|
|
@@ -365,6 +365,9 @@ def get_private_key_from_eth_signature(eth_signature: str) -> int:
|
|
|
365
365
|
|
|
366
366
|
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)
|
|
367
367
|
|
|
368
|
+
## Breaking changes
|
|
369
|
+
|
|
370
|
+
For a detailed list of breaking changes, please refer to the [MIGRATION.md](MIGRATION.md) file.
|
|
368
371
|
|
|
369
372
|
## Contributing
|
|
370
373
|
|
|
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
5
5
|
|
|
6
6
|
[tool.poetry]
|
|
7
7
|
name = "x10-python-trading-starknet"
|
|
8
|
-
version = "
|
|
8
|
+
version = "2.0.0"
|
|
9
9
|
description = "Python client for X10 API"
|
|
10
10
|
authors = ["X10 <tech@ex10.org>"]
|
|
11
11
|
repository = "https://github.com/x10xchange/python_sdk"
|
|
@@ -29,9 +29,9 @@ pydantic = ">=2.9.0"
|
|
|
29
29
|
python = "^3.10"
|
|
30
30
|
pyyaml = ">=6.0.1"
|
|
31
31
|
sortedcontainers = ">=2.4.0"
|
|
32
|
-
strenum = "
|
|
33
|
-
tenacity = "
|
|
34
|
-
websockets = ">=12.0
|
|
32
|
+
strenum = ">=0.4.15"
|
|
33
|
+
tenacity = ">=9.1.2"
|
|
34
|
+
websockets = ">=12.0"
|
|
35
35
|
|
|
36
36
|
[tool.poetry.group.dev.dependencies]
|
|
37
37
|
black = "==23.12.0"
|
|
@@ -52,7 +52,7 @@ python-dotenv = "==1.0.1"
|
|
|
52
52
|
safety = "==3.5.1"
|
|
53
53
|
tox = "==4.11.4"
|
|
54
54
|
types-pyyaml = "==6.0.12.12"
|
|
55
|
-
typing-extensions = "
|
|
55
|
+
typing-extensions = "==4.15.0"
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
[tool.mypy]
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import dataclasses
|
|
3
2
|
import time
|
|
3
|
+
from dataclasses import dataclass
|
|
4
4
|
from decimal import Decimal
|
|
5
|
-
from typing import Awaitable, Dict,
|
|
5
|
+
from typing import Awaitable, Dict, Optional, cast
|
|
6
6
|
|
|
7
|
-
from x10.
|
|
8
|
-
from x10.
|
|
9
|
-
from x10.
|
|
10
|
-
from x10.
|
|
11
|
-
from x10.
|
|
7
|
+
from x10.clients.rest.modules.info_module import InfoModule
|
|
8
|
+
from x10.clients.rest.modules.order_management_module import OrderManagementModule
|
|
9
|
+
from x10.clients.stream import StreamClient, StreamConnection
|
|
10
|
+
from x10.config import Config
|
|
11
|
+
from x10.core.stark_account import StarkPerpetualAccount
|
|
12
|
+
from x10.errors import SdkError, ValidationError
|
|
13
|
+
from x10.models.account import AccountStreamDataModel
|
|
14
|
+
from x10.models.http import WrappedStreamResponseModel
|
|
15
|
+
from x10.models.market import MarketModel
|
|
16
|
+
from x10.models.order import (
|
|
12
17
|
NewOrderModel,
|
|
13
18
|
OpenOrderModel,
|
|
14
19
|
OrderSide,
|
|
@@ -16,13 +21,7 @@ from x10.perpetual.orders import (
|
|
|
16
21
|
OrderType,
|
|
17
22
|
TimeInForce,
|
|
18
23
|
)
|
|
19
|
-
from x10.
|
|
20
|
-
PerpetualStreamConnection,
|
|
21
|
-
)
|
|
22
|
-
from x10.perpetual.stream_client.stream_client import PerpetualStreamClient
|
|
23
|
-
from x10.perpetual.trading_client.info_markets_module import InfoMarketsModule
|
|
24
|
-
from x10.perpetual.trading_client.order_management_module import OrderManagementModule
|
|
25
|
-
from x10.utils.http import WrappedStreamResponse
|
|
24
|
+
from x10.signing.order_object import create_order_object
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
def condition_to_awaitable(condition: asyncio.Condition) -> Awaitable:
|
|
@@ -51,21 +50,21 @@ class TimedOpenOrderModel(OpenOrderModel):
|
|
|
51
50
|
)
|
|
52
51
|
|
|
53
52
|
|
|
54
|
-
@
|
|
53
|
+
@dataclass
|
|
55
54
|
class TimedCancel:
|
|
56
55
|
start_nanos: int
|
|
57
56
|
end_nanos: int
|
|
58
57
|
operation_ms: float
|
|
59
58
|
|
|
60
59
|
|
|
61
|
-
@
|
|
60
|
+
@dataclass
|
|
62
61
|
class OrderWaiter:
|
|
63
62
|
condition: asyncio.Condition
|
|
64
63
|
open_order: None | TimedOpenOrderModel
|
|
65
64
|
start_nanos: int
|
|
66
65
|
|
|
67
66
|
|
|
68
|
-
@
|
|
67
|
+
@dataclass
|
|
69
68
|
class CancelWaiter:
|
|
70
69
|
condition: asyncio.Condition
|
|
71
70
|
start_nanos: int
|
|
@@ -73,28 +72,31 @@ class CancelWaiter:
|
|
|
73
72
|
|
|
74
73
|
|
|
75
74
|
class BlockingTradingClient:
|
|
76
|
-
|
|
75
|
+
"""
|
|
76
|
+
A client for placing orders and receiving updates in a blocking manner.
|
|
77
|
+
Waits for the confirmation from the WS stream after placing or canceling an order.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, config: Config, account: StarkPerpetualAccount):
|
|
77
81
|
if not asyncio.get_event_loop().is_running():
|
|
78
|
-
raise
|
|
82
|
+
raise SdkError(
|
|
79
83
|
"BlockingTradingClient must be initialized from an async function, use BlockingTradingClient.create()"
|
|
80
84
|
)
|
|
81
|
-
|
|
85
|
+
|
|
86
|
+
self.__config = config
|
|
82
87
|
self.__account = account
|
|
83
|
-
self.
|
|
84
|
-
self.__orders_module = OrderManagementModule(
|
|
85
|
-
self.__markets:
|
|
86
|
-
self.__stream_client:
|
|
87
|
-
self.__account_stream:
|
|
88
|
-
None,
|
|
89
|
-
PerpetualStreamConnection[WrappedStreamResponse[AccountStreamDataModel]],
|
|
90
|
-
] = None
|
|
88
|
+
self.__info_module = InfoModule(config, api_key=account.api_key)
|
|
89
|
+
self.__orders_module = OrderManagementModule(config, api_key=account.api_key)
|
|
90
|
+
self.__markets: Optional[Dict[str, MarketModel]] = None
|
|
91
|
+
self.__stream_client: StreamClient = StreamClient(api_url=config.endpoints.stream_url)
|
|
92
|
+
self.__account_stream: Optional[StreamConnection[WrappedStreamResponseModel[AccountStreamDataModel]]] = None
|
|
91
93
|
self.__order_waiters: Dict[str, OrderWaiter] = {}
|
|
92
94
|
self.__cancel_waiters: Dict[str, CancelWaiter] = {}
|
|
93
|
-
self.__stream_task = asyncio.create_task(self.
|
|
95
|
+
self.__stream_task = asyncio.create_task(self.__order_stream())
|
|
94
96
|
|
|
95
97
|
@staticmethod
|
|
96
|
-
async def create(
|
|
97
|
-
client = BlockingTradingClient(
|
|
98
|
+
async def create(config: Config, account: StarkPerpetualAccount) -> "BlockingTradingClient":
|
|
99
|
+
client = BlockingTradingClient(config, account)
|
|
98
100
|
await client.__stream_client.subscribe_to_account_updates(account.api_key)
|
|
99
101
|
return client
|
|
100
102
|
|
|
@@ -127,7 +129,7 @@ class BlockingTradingClient:
|
|
|
127
129
|
else:
|
|
128
130
|
await self.__handle_update(order)
|
|
129
131
|
|
|
130
|
-
async def
|
|
132
|
+
async def __order_stream(self):
|
|
131
133
|
self.__account_stream = await self.__stream_client.subscribe_to_account_updates(self.__account.api_key)
|
|
132
134
|
async for event in self.__account_stream:
|
|
133
135
|
if not (event.data and event.data.orders):
|
|
@@ -135,7 +137,7 @@ class BlockingTradingClient:
|
|
|
135
137
|
for order in event.data.orders:
|
|
136
138
|
await self.__handle_order(order)
|
|
137
139
|
print("Order stream closed, reconnecting...")
|
|
138
|
-
await self.
|
|
140
|
+
await self.__order_stream()
|
|
139
141
|
|
|
140
142
|
async def cancel_order(self, order_external_id: str) -> TimedCancel:
|
|
141
143
|
awaitable: Awaitable
|
|
@@ -169,10 +171,10 @@ class BlockingTradingClient:
|
|
|
169
171
|
|
|
170
172
|
async def get_markets(self) -> Dict[str, MarketModel]:
|
|
171
173
|
if not self.__markets:
|
|
172
|
-
markets = await self.
|
|
174
|
+
markets = await self.__info_module.get_markets()
|
|
173
175
|
market_data = markets.data
|
|
174
176
|
if not market_data:
|
|
175
|
-
raise
|
|
177
|
+
raise ValidationError("Core market data is empty, check your connection or API key.")
|
|
176
178
|
self.__markets = {m.name: m for m in market_data}
|
|
177
179
|
return self.__markets
|
|
178
180
|
|
|
@@ -196,6 +198,7 @@ class BlockingTradingClient:
|
|
|
196
198
|
amount_of_synthetic: Decimal,
|
|
197
199
|
price: Decimal,
|
|
198
200
|
side: OrderSide,
|
|
201
|
+
taker_fee: Decimal,
|
|
199
202
|
post_only: bool = False,
|
|
200
203
|
previous_order_external_id: str | None = None,
|
|
201
204
|
external_id: str | None = None,
|
|
@@ -207,7 +210,7 @@ class BlockingTradingClient:
|
|
|
207
210
|
) -> TimedOpenOrderModel:
|
|
208
211
|
market = (await self.get_markets()).get(market_name)
|
|
209
212
|
if not market:
|
|
210
|
-
raise
|
|
213
|
+
raise ValidationError(f"Market '{market_name}' not found.")
|
|
211
214
|
|
|
212
215
|
order: NewOrderModel = create_order_object(
|
|
213
216
|
account=self.__account,
|
|
@@ -219,15 +222,16 @@ class BlockingTradingClient:
|
|
|
219
222
|
post_only=post_only,
|
|
220
223
|
reduce_only=reduce_only,
|
|
221
224
|
previous_order_external_id=previous_order_external_id,
|
|
222
|
-
starknet_domain=self.
|
|
225
|
+
starknet_domain=self.__config.signing.starknet_domain,
|
|
223
226
|
order_external_id=external_id,
|
|
224
227
|
builder_fee=builder_fee,
|
|
225
228
|
builder_id=builder_id,
|
|
226
229
|
time_in_force=time_in_force,
|
|
230
|
+
taker_fee=taker_fee,
|
|
227
231
|
)
|
|
228
232
|
|
|
229
233
|
if order.id in self.__order_waiters:
|
|
230
|
-
raise
|
|
234
|
+
raise ValidationError(f"order with {order.id} hash already placed")
|
|
231
235
|
|
|
232
236
|
self.__order_waiters[order.id] = OrderWaiter(asyncio.Condition(), None, start_nanos=time.time_ns())
|
|
233
237
|
placed_order_task = asyncio.create_task(self.__orders_module.place_order(order))
|
|
@@ -243,7 +247,7 @@ class BlockingTradingClient:
|
|
|
243
247
|
open_model = self.__order_waiters[order.id].open_order
|
|
244
248
|
del self.__order_waiters[order.id]
|
|
245
249
|
if not open_model:
|
|
246
|
-
raise
|
|
250
|
+
raise ValidationError("No Fill or Placement received for order")
|
|
247
251
|
return open_model
|
|
248
252
|
|
|
249
253
|
async def close(self):
|
|
@@ -251,3 +255,7 @@ class BlockingTradingClient:
|
|
|
251
255
|
self.__stream_task.cancel()
|
|
252
256
|
if self.__account_stream:
|
|
253
257
|
await self.__account_stream.close()
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def config(self):
|
|
261
|
+
return self.__config
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from x10.clients.onboarding.onboarding_client import OnboardingClient # noqa: F401
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from x10.clients.onboarding.modules.base_module import BaseModule
|
|
2
|
+
from x10.errors import ValidationError
|
|
3
|
+
from x10.models.account import ApiKeyRequestModel, ApiKeyResponseModel
|
|
4
|
+
from x10.signing.onboarding import sign_api_request
|
|
5
|
+
from x10.utils.http import RequestHeader, send_post_request
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AccountModule(BaseModule):
|
|
9
|
+
async def create_api_key(self, *, account_id: int, description: str) -> str:
|
|
10
|
+
request_path = "/api/v1/user/account/api-key"
|
|
11
|
+
signature = sign_api_request(request_path, self._sign_message)
|
|
12
|
+
headers: dict[str, str] = {
|
|
13
|
+
RequestHeader.AUTH_L1_SIGNATURE: signature.value,
|
|
14
|
+
RequestHeader.AUTH_L1_MESSAGE_TIME: signature.time,
|
|
15
|
+
RequestHeader.AUTH_ACTIVE_ACCOUNT: str(account_id),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
payload = ApiKeyRequestModel(description=description)
|
|
19
|
+
url = self._get_url(request_path)
|
|
20
|
+
response = await send_post_request(
|
|
21
|
+
await self._get_session(),
|
|
22
|
+
url,
|
|
23
|
+
ApiKeyResponseModel,
|
|
24
|
+
json=payload.to_api_request_json(),
|
|
25
|
+
request_headers=headers,
|
|
26
|
+
)
|
|
27
|
+
response_data = response.data
|
|
28
|
+
|
|
29
|
+
if response_data is None:
|
|
30
|
+
raise ValidationError("No API key data returned from onboarding")
|
|
31
|
+
|
|
32
|
+
return response_data.key
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from aiohttp.web_exceptions import HTTPConflict
|
|
2
|
+
|
|
3
|
+
from x10.clients.onboarding.modules.base_module import BaseModule
|
|
4
|
+
from x10.errors import SdkError, ValidationError
|
|
5
|
+
from x10.models.account import AccountModel
|
|
6
|
+
from x10.models.client import OnboardedClientModel
|
|
7
|
+
from x10.signing.onboarding import (
|
|
8
|
+
OnBoardedAccount,
|
|
9
|
+
get_l2_keys_from_l1_account,
|
|
10
|
+
get_onboarding_payload,
|
|
11
|
+
get_sub_account_creation_payload,
|
|
12
|
+
sign_api_request,
|
|
13
|
+
)
|
|
14
|
+
from x10.utils.http import RequestHeader, send_post_request
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SubAccountExists(SdkError):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AuthModule(BaseModule):
|
|
22
|
+
async def onboard_client(self, *, referral_code: str | None = None) -> OnBoardedAccount:
|
|
23
|
+
l2_key_pair = get_l2_keys_from_l1_account(
|
|
24
|
+
account_index=0,
|
|
25
|
+
account_address=self._get_account_address(),
|
|
26
|
+
signing_domain=self._get_config().signing.signing_domain,
|
|
27
|
+
sign_message=self._sign_message,
|
|
28
|
+
)
|
|
29
|
+
payload = get_onboarding_payload(
|
|
30
|
+
account_address=self._get_account_address(),
|
|
31
|
+
signing_domain=self._get_config().signing.signing_domain,
|
|
32
|
+
key_pair=l2_key_pair,
|
|
33
|
+
referral_code=referral_code,
|
|
34
|
+
host=self._get_config().endpoints.onboarding_url,
|
|
35
|
+
sign_message=self._sign_message,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
url = self._get_url("/auth/onboard")
|
|
39
|
+
onboarding_response = await send_post_request(
|
|
40
|
+
await self._get_session(), url, OnboardedClientModel, json=payload.to_json()
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
onboarded_client = onboarding_response.data
|
|
44
|
+
|
|
45
|
+
if onboarded_client is None:
|
|
46
|
+
raise ValidationError("No account data returned from onboarding")
|
|
47
|
+
|
|
48
|
+
return OnBoardedAccount(account=onboarded_client.default_account, l2_key_pair=l2_key_pair)
|
|
49
|
+
|
|
50
|
+
async def onboard_subaccount(self, *, account_index: int, description: str):
|
|
51
|
+
request_path = "/auth/onboard/subaccount"
|
|
52
|
+
signature = sign_api_request(request_path, self._sign_message)
|
|
53
|
+
headers: dict[str, str] = {
|
|
54
|
+
RequestHeader.AUTH_L1_SIGNATURE: signature.value,
|
|
55
|
+
RequestHeader.AUTH_L1_MESSAGE_TIME: signature.time,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
key_pair = get_l2_keys_from_l1_account(
|
|
59
|
+
account_index=account_index,
|
|
60
|
+
account_address=self._get_account_address(),
|
|
61
|
+
signing_domain=self._get_config().signing.signing_domain,
|
|
62
|
+
sign_message=self._sign_message,
|
|
63
|
+
)
|
|
64
|
+
payload = get_sub_account_creation_payload(
|
|
65
|
+
account_index=account_index,
|
|
66
|
+
l1_address=self._get_account_address(),
|
|
67
|
+
key_pair=key_pair,
|
|
68
|
+
description=description,
|
|
69
|
+
host=self._get_config().endpoints.onboarding_url,
|
|
70
|
+
)
|
|
71
|
+
url = self._get_url(request_path)
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
onboarding_response = await send_post_request(
|
|
75
|
+
await self._get_session(),
|
|
76
|
+
url,
|
|
77
|
+
AccountModel,
|
|
78
|
+
json=payload.to_json(),
|
|
79
|
+
request_headers=headers,
|
|
80
|
+
response_code_to_exception={HTTPConflict.status_code: SubAccountExists},
|
|
81
|
+
)
|
|
82
|
+
onboarded_account = onboarding_response.data
|
|
83
|
+
except SubAccountExists:
|
|
84
|
+
raise ValidationError("Subaccount already exists")
|
|
85
|
+
|
|
86
|
+
if onboarded_account is None:
|
|
87
|
+
raise ValidationError("No account data returned from onboarding")
|
|
88
|
+
|
|
89
|
+
return OnBoardedAccount(account=onboarded_account, l2_key_pair=key_pair)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from typing import Dict, Optional
|
|
2
|
+
|
|
3
|
+
import aiohttp
|
|
4
|
+
from aiohttp import ClientTimeout
|
|
5
|
+
from eth_account.messages import SignableMessage
|
|
6
|
+
from eth_typing import ChecksumAddress
|
|
7
|
+
|
|
8
|
+
from x10.config import Config
|
|
9
|
+
from x10.signing.onboarding import SignMessageCallback
|
|
10
|
+
from x10.utils.http import get_url
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseModule:
|
|
14
|
+
__config: Config
|
|
15
|
+
__account_address: ChecksumAddress
|
|
16
|
+
__sign_message: SignMessageCallback
|
|
17
|
+
__session: Optional[aiohttp.ClientSession]
|
|
18
|
+
|
|
19
|
+
def __init__(self, config: Config, *, account_address: ChecksumAddress, sign_message: SignMessageCallback):
|
|
20
|
+
super().__init__()
|
|
21
|
+
|
|
22
|
+
self.__config = config
|
|
23
|
+
self.__account_address = account_address
|
|
24
|
+
self.__sign_message = sign_message
|
|
25
|
+
self.__session = None
|
|
26
|
+
|
|
27
|
+
def _get_url(self, path: str, *, query: Optional[Dict] = None, **path_params) -> str:
|
|
28
|
+
return get_url(f"{self.__config.endpoints.onboarding_url}{path}", query=query, **path_params)
|
|
29
|
+
|
|
30
|
+
def _get_config(self) -> Config:
|
|
31
|
+
return self.__config
|
|
32
|
+
|
|
33
|
+
def _get_account_address(self) -> ChecksumAddress:
|
|
34
|
+
return self.__account_address
|
|
35
|
+
|
|
36
|
+
def _sign_message(self, msg: SignableMessage) -> str:
|
|
37
|
+
return self.__sign_message(msg)
|
|
38
|
+
|
|
39
|
+
async def _get_session(self) -> aiohttp.ClientSession:
|
|
40
|
+
if self.__session is None:
|
|
41
|
+
created_session = aiohttp.ClientSession(
|
|
42
|
+
timeout=ClientTimeout(total=self.__config.defaults.request_timeout_seconds)
|
|
43
|
+
)
|
|
44
|
+
self.__session = created_session
|
|
45
|
+
|
|
46
|
+
return self.__session
|
|
47
|
+
|
|
48
|
+
async def close_session(self):
|
|
49
|
+
if self.__session:
|
|
50
|
+
await self.__session.close()
|
|
51
|
+
self.__session = None
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from eth_typing import ChecksumAddress
|
|
2
|
+
|
|
3
|
+
from x10.clients.onboarding.modules.account_module import AccountModule
|
|
4
|
+
from x10.clients.onboarding.modules.auth_module import AuthModule
|
|
5
|
+
from x10.config import Config
|
|
6
|
+
from x10.signing.onboarding import SignMessageCallback
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OnboardingClient:
|
|
10
|
+
__account_module: AccountModule
|
|
11
|
+
__auth_module: AuthModule
|
|
12
|
+
|
|
13
|
+
async def close(self):
|
|
14
|
+
await self.__account_module.close_session()
|
|
15
|
+
await self.__auth_module.close_session()
|
|
16
|
+
|
|
17
|
+
async def __aenter__(self):
|
|
18
|
+
return self
|
|
19
|
+
|
|
20
|
+
async def __aexit__(self, exc_type, exc_value, traceback):
|
|
21
|
+
await self.close()
|
|
22
|
+
|
|
23
|
+
def __init__(self, config: Config, *, account_address: ChecksumAddress, sign_message: SignMessageCallback):
|
|
24
|
+
self.__account_module = AccountModule(config, account_address=account_address, sign_message=sign_message)
|
|
25
|
+
self.__auth_module = AuthModule(config, account_address=account_address, sign_message=sign_message)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def account(self):
|
|
29
|
+
return self.__account_module
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def auth(self):
|
|
33
|
+
return self.__auth_module
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from x10.clients.rest.rest_api_client import RestApiClient # noqa: F401
|