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.
Files changed (82) hide show
  1. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/PKG-INFO +8 -5
  2. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/README.md +4 -1
  3. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/pyproject.toml +5 -5
  4. x10_python_trading_starknet-2.0.0/x10/clients/blocking/__init__.py +3 -0
  5. 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
  6. x10_python_trading_starknet-2.0.0/x10/clients/onboarding/__init__.py +1 -0
  7. x10_python_trading_starknet-2.0.0/x10/clients/onboarding/modules/account_module.py +32 -0
  8. x10_python_trading_starknet-2.0.0/x10/clients/onboarding/modules/auth_module.py +89 -0
  9. x10_python_trading_starknet-2.0.0/x10/clients/onboarding/modules/base_module.py +51 -0
  10. x10_python_trading_starknet-2.0.0/x10/clients/onboarding/onboarding_client.py +33 -0
  11. x10_python_trading_starknet-2.0.0/x10/clients/rest/__init__.py +1 -0
  12. {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
  13. {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
  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
  15. {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
  16. {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
  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
  18. 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
  19. x10_python_trading_starknet-2.0.0/x10/clients/stream/__init__.py +2 -0
  20. {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
  21. 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
  22. x10_python_trading_starknet-2.0.0/x10/config.py +99 -0
  23. x10_python_trading_starknet-1.3.1/x10/perpetual/amounts.py → x10_python_trading_starknet-2.0.0/x10/core/amount.py +4 -13
  24. x10_python_trading_starknet-2.0.0/x10/core/stark_account.py +43 -0
  25. x10_python_trading_starknet-2.0.0/x10/errors.py +22 -0
  26. x10_python_trading_starknet-2.0.0/x10/models/__init__.py +0 -0
  27. x10_python_trading_starknet-2.0.0/x10/models/account.py +40 -0
  28. x10_python_trading_starknet-1.3.1/x10/perpetual/assets.py → x10_python_trading_starknet-2.0.0/x10/models/asset.py +4 -3
  29. x10_python_trading_starknet-1.3.1/x10/perpetual/balances.py → x10_python_trading_starknet-2.0.0/x10/models/balance.py +1 -1
  30. x10_python_trading_starknet-2.0.0/x10/models/bridge.py +17 -0
  31. x10_python_trading_starknet-1.3.1/x10/perpetual/candles.py → x10_python_trading_starknet-2.0.0/x10/models/candle.py +1 -1
  32. x10_python_trading_starknet-2.0.0/x10/models/client.py +14 -0
  33. x10_python_trading_starknet-2.0.0/x10/models/fee.py +10 -0
  34. 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
  35. x10_python_trading_starknet-2.0.0/x10/models/http.py +60 -0
  36. x10_python_trading_starknet-1.3.1/x10/perpetual/markets.py → x10_python_trading_starknet-2.0.0/x10/models/market.py +4 -4
  37. x10_python_trading_starknet-1.3.1/x10/perpetual/orders.py → x10_python_trading_starknet-2.0.0/x10/models/order.py +9 -2
  38. x10_python_trading_starknet-1.3.1/x10/perpetual/orderbooks.py → x10_python_trading_starknet-2.0.0/x10/models/orderbook.py +1 -1
  39. x10_python_trading_starknet-1.3.1/x10/perpetual/positions.py → x10_python_trading_starknet-2.0.0/x10/models/position.py +1 -1
  40. x10_python_trading_starknet-2.0.0/x10/models/settings.py +5 -0
  41. x10_python_trading_starknet-2.0.0/x10/models/testnet.py +5 -0
  42. x10_python_trading_starknet-1.3.1/x10/perpetual/trades.py → x10_python_trading_starknet-2.0.0/x10/models/trade.py +2 -2
  43. x10_python_trading_starknet-1.3.1/x10/perpetual/transfers.py → x10_python_trading_starknet-2.0.0/x10/models/transfer.py +4 -5
  44. x10_python_trading_starknet-2.0.0/x10/models/vault.py +20 -0
  45. x10_python_trading_starknet-1.3.1/x10/perpetual/withdrawals.py → x10_python_trading_starknet-2.0.0/x10/models/withdrawal.py +6 -6
  46. x10_python_trading_starknet-2.0.0/x10/py.typed +0 -0
  47. x10_python_trading_starknet-2.0.0/x10/signing/__init__.py +0 -0
  48. {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
  49. {x10_python_trading_starknet-1.3.1/x10/perpetual/user_client → x10_python_trading_starknet-2.0.0/x10/signing}/onboarding.py +56 -27
  50. {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/order_object.py +32 -30
  51. {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/order_object_settlement.py +15 -21
  52. {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/transfer_object.py +18 -24
  53. {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/signing}/withdrawal_object.py +20 -26
  54. x10_python_trading_starknet-2.0.0/x10/tools/__init__.py +0 -0
  55. {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/tools}/orderbook.py +27 -28
  56. x10_python_trading_starknet-2.0.0/x10/utils/__init__.py +0 -0
  57. x10_python_trading_starknet-2.0.0/x10/utils/date.py +23 -0
  58. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/http.py +39 -82
  59. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/order.py +1 -1
  60. x10_python_trading_starknet-2.0.0/x10/version.py +3 -0
  61. x10_python_trading_starknet-1.3.1/x10/config.py +0 -12
  62. x10_python_trading_starknet-1.3.1/x10/errors.py +0 -2
  63. x10_python_trading_starknet-1.3.1/x10/perpetual/accounts.py +0 -86
  64. x10_python_trading_starknet-1.3.1/x10/perpetual/bridges.py +0 -17
  65. x10_python_trading_starknet-1.3.1/x10/perpetual/clients.py +0 -8
  66. x10_python_trading_starknet-1.3.1/x10/perpetual/configuration.py +0 -68
  67. x10_python_trading_starknet-1.3.1/x10/perpetual/fees.py +0 -18
  68. x10_python_trading_starknet-1.3.1/x10/perpetual/stream_client/__init__.py +0 -3
  69. x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client/__init__.py +0 -3
  70. x10_python_trading_starknet-1.3.1/x10/perpetual/trading_client/info_module.py +0 -29
  71. x10_python_trading_starknet-1.3.1/x10/perpetual/user_client/user_client.py +0 -201
  72. x10_python_trading_starknet-1.3.1/x10/utils/date.py +0 -13
  73. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/LICENSE +0 -0
  74. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/__init__.py +0 -0
  75. {x10_python_trading_starknet-1.3.1/x10/perpetual → x10_python_trading_starknet-2.0.0/x10/clients}/__init__.py +0 -0
  76. {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
  77. {x10_python_trading_starknet-1.3.1/x10/utils → x10_python_trading_starknet-2.0.0/x10/clients/rest/modules}/__init__.py +0 -0
  78. /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
  79. /x10_python_trading_starknet-1.3.1/x10/utils/model.py → /x10_python_trading_starknet-2.0.0/x10/models/base.py +0 -0
  80. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/log.py +0 -0
  81. {x10_python_trading_starknet-1.3.1 → x10_python_trading_starknet-2.0.0}/x10/utils/nonce.py +0 -0
  82. {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: 1.3.1
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,<0.5.0)
24
- Requires-Dist: tenacity (>=9.1.2,<10.0.0)
25
- Requires-Dist: websockets (>=12.0,<14.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 = "1.3.1"
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 = "^0.4.15"
33
- tenacity = "^9.1.2"
34
- websockets = ">=12.0,<14.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 = ">=4.9.0"
55
+ typing-extensions = "==4.15.0"
56
56
 
57
57
 
58
58
  [tool.mypy]
@@ -0,0 +1,3 @@
1
+ from x10.clients.blocking.blocking_trading_client import ( # noqa: F401
2
+ BlockingTradingClient,
3
+ )
@@ -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, Union, cast
5
+ from typing import Awaitable, Dict, Optional, cast
6
6
 
7
- from x10.perpetual.accounts import AccountStreamDataModel, StarkPerpetualAccount
8
- from x10.perpetual.configuration import EndpointConfig
9
- from x10.perpetual.markets import MarketModel
10
- from x10.perpetual.order_object import create_order_object
11
- from x10.perpetual.orders import (
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.perpetual.stream_client.perpetual_stream_connection import (
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
- @dataclasses.dataclass
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
- @dataclasses.dataclass
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
- @dataclasses.dataclass
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
- def __init__(self, endpoint_config: EndpointConfig, account: StarkPerpetualAccount):
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 RuntimeError(
82
+ raise SdkError(
79
83
  "BlockingTradingClient must be initialized from an async function, use BlockingTradingClient.create()"
80
84
  )
81
- self.__endpoint_config = endpoint_config
85
+
86
+ self.__config = config
82
87
  self.__account = account
83
- self.__market_module = InfoMarketsModule(endpoint_config, api_key=account.api_key)
84
- self.__orders_module = OrderManagementModule(endpoint_config, api_key=account.api_key)
85
- self.__markets: Union[None, Dict[str, MarketModel]] = None
86
- self.__stream_client: PerpetualStreamClient = PerpetualStreamClient(api_url=endpoint_config.stream_url)
87
- self.__account_stream: Union[
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.___order_stream())
95
+ self.__stream_task = asyncio.create_task(self.__order_stream())
94
96
 
95
97
  @staticmethod
96
- async def create(endpoint_config: EndpointConfig, account: StarkPerpetualAccount) -> "BlockingTradingClient":
97
- client = BlockingTradingClient(endpoint_config, account)
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 ___order_stream(self):
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.___order_stream()
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.__market_module.get_markets()
174
+ markets = await self.__info_module.get_markets()
173
175
  market_data = markets.data
174
176
  if not market_data:
175
- raise ValueError("Core market data is empty, check your connection or API key.")
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 ValueError(f"Market '{market_name}' not found.")
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.__endpoint_config.starknet_domain,
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 ValueError(f"order with {order.id} hash already placed")
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 ValueError("No Fill or Placement received for order")
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