crypticorn 2.0.0__py3-none-any.whl → 2.1.0__py3-none-any.whl

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.
@@ -1,12 +1,13 @@
1
- from fastapi import Depends, HTTPException, status
1
+ from fastapi import Depends, HTTPException, Query, status
2
2
  from fastapi.security import HTTPAuthorizationCredentials
3
3
  from typing_extensions import Annotated, Doc
4
+ import json
4
5
 
5
6
  from crypticorn.auth import AuthClient, Verify200Response
6
- from crypticorn.auth.client.exceptions import UnauthorizedException
7
+ from crypticorn.auth.client.exceptions import ApiException
7
8
  from crypticorn.common import (
8
9
  ApiError,
9
- ApiScope,
10
+ Scope,
10
11
  ApiVersion,
11
12
  BaseURL,
12
13
  Domain,
@@ -41,29 +42,16 @@ class AuthHandler:
41
42
  self.whitelist = whitelist
42
43
  self.auth_client = AuthClient(base_url=base_url, api_version=api_version)
43
44
 
44
- self.invalid_scopes_exception = HTTPException(
45
- status_code=status.HTTP_403_FORBIDDEN,
46
- detail=ApiError.INSUFFICIENT_SCOPES.identifier,
47
- )
48
45
  self.no_credentials_exception = HTTPException(
49
46
  status_code=status.HTTP_401_UNAUTHORIZED,
50
47
  detail=ApiError.NO_CREDENTIALS.identifier,
51
48
  )
52
- self.invalid_api_key_exception = HTTPException(
53
- status_code=status.HTTP_401_UNAUTHORIZED,
54
- detail=ApiError.INVALID_API_KEY.identifier,
55
- )
56
- self.invalid_bearer_exception = HTTPException(
57
- status_code=status.HTTP_401_UNAUTHORIZED,
58
- detail=ApiError.INVALID_BEARER.identifier,
59
- )
60
49
 
61
50
  async def _verify_api_key(self, api_key: str) -> None:
62
51
  """
63
52
  Verifies the API key.
64
53
  """
65
- # TODO: Implement in auth service
66
- return NotImplementedError()
54
+ return await self.auth_client.login.verify_api_key(api_key)
67
55
 
68
56
  async def _verify_bearer(
69
57
  self, bearer: HTTPAuthorizationCredentials
@@ -74,32 +62,60 @@ class AuthHandler:
74
62
  self.auth_client.config.access_token = bearer.credentials
75
63
  return await self.auth_client.login.verify()
76
64
 
77
- async def _check_scopes(
78
- self, api_scopes: list[ApiScope], user_scopes: list[ApiScope]
65
+ async def _validate_scopes(
66
+ self, api_scopes: list[Scope], user_scopes: list[Scope]
79
67
  ) -> bool:
80
68
  """
81
69
  Checks if the user scopes are a subset of the API scopes.
82
70
  """
83
- return set(api_scopes).issubset(user_scopes)
71
+ if not set(api_scopes).issubset(user_scopes):
72
+ raise HTTPException(
73
+ status_code=status.HTTP_403_FORBIDDEN,
74
+ detail=ApiError.INSUFFICIENT_SCOPES.identifier,
75
+ )
76
+
77
+ async def _extract_message(self, e: ApiException) -> str:
78
+ """
79
+ Tries to extract the message from the body of the exception.
80
+ """
81
+ try:
82
+ load = json.loads(e.body)
83
+ except (json.JSONDecodeError, TypeError):
84
+ return e.body
85
+ else:
86
+ common_keys = ["message"]
87
+ for key in common_keys:
88
+ if key in load:
89
+ return load[key]
90
+ return load
91
+
92
+ async def _handle_exception(self, e: Exception) -> HTTPException:
93
+ """
94
+ Handles exceptions and returns a HTTPException with the appropriate status code and detail.
95
+ """
96
+ if isinstance(e, ApiException):
97
+ return HTTPException(
98
+ status_code=e.status,
99
+ detail=await self._extract_message(e),
100
+ )
101
+ elif isinstance(e, HTTPException):
102
+ return e
103
+ else:
104
+ return HTTPException(
105
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
106
+ detail=str(e),
107
+ )
84
108
 
85
109
  async def api_key_auth(
86
110
  self,
87
111
  api_key: Annotated[str | None, Depends(apikey_header)] = None,
88
- scopes: list[ApiScope] = [],
112
+ scopes: list[Scope] = [],
89
113
  ) -> Verify200Response:
90
114
  """
91
115
  Verifies the API key and checks if the user scopes are a subset of the API scopes.
116
+ Use this function if you only want to allow access via the API key.
92
117
  """
93
- if not api_key:
94
- raise self.no_credentials_exception
95
- try:
96
- res = await self._verify_api_key(api_key)
97
- except UnauthorizedException as e:
98
- raise self.invalid_api_key_exception
99
- valid_scopes = await self._check_scopes(scopes, res.scopes)
100
- if not valid_scopes:
101
- raise self.invalid_scopes_exception
102
- return res
118
+ return await self.combined_auth(bearer=None, api_key=api_key, scopes=scopes)
103
119
 
104
120
  async def bearer_auth(
105
121
  self,
@@ -107,22 +123,13 @@ class AuthHandler:
107
123
  HTTPAuthorizationCredentials | None,
108
124
  Depends(http_bearer),
109
125
  ] = None,
110
- scopes: list[ApiScope] = [],
126
+ scopes: list[Scope] = [],
111
127
  ) -> Verify200Response:
112
128
  """
113
129
  Verifies the bearer token and checks if the user scopes are a subset of the API scopes.
130
+ Use this function if you only want to allow access via the bearer token.
114
131
  """
115
- if not bearer:
116
- raise self.no_credentials_exception
117
-
118
- try:
119
- res = await self._verify_bearer(bearer)
120
- except UnauthorizedException as e:
121
- raise self.invalid_bearer_exception
122
- valid_scopes = await self._check_scopes(scopes, res.scopes)
123
- if not valid_scopes:
124
- raise self.invalid_scopes_exception
125
- return res
132
+ return await self.combined_auth(bearer=bearer, api_key=None, scopes=scopes)
126
133
 
127
134
  async def combined_auth(
128
135
  self,
@@ -130,11 +137,12 @@ class AuthHandler:
130
137
  HTTPAuthorizationCredentials | None, Depends(http_bearer)
131
138
  ] = None,
132
139
  api_key: Annotated[str | None, Depends(apikey_header)] = None,
133
- scopes: list[ApiScope] = [],
140
+ scopes: list[Scope] = [],
134
141
  ) -> Verify200Response:
135
142
  """
136
- Verifies the bearer token and API key and checks if the user scopes are a subset of the API scopes.
143
+ Verifies the bearer token and/or API key and checks if the user scopes are a subset of the API scopes.
137
144
  Returns early on the first successful verification, otherwise tries all available tokens.
145
+ Use this function if you want to allow access via either the bearer token or the API key.
138
146
  """
139
147
  tokens = [bearer, api_key]
140
148
 
@@ -151,13 +159,59 @@ class AuthHandler:
151
159
  if res is None:
152
160
  continue
153
161
  if scopes:
154
- valid_scopes = await self._check_scopes(scopes, res.scopes)
155
- if not valid_scopes:
156
- raise self.invalid_scopes_exception
162
+ await self._validate_scopes(
163
+ scopes, [Scope.from_str(scope) for scope in res.scopes]
164
+ )
157
165
  return res
158
166
 
159
- except UnauthorizedException as e:
160
- last_error = e
167
+ except Exception as e:
168
+ last_error = await self._handle_exception(e)
161
169
  continue
162
170
 
163
171
  raise last_error or self.no_credentials_exception
172
+
173
+ async def ws_api_key_auth(
174
+ self,
175
+ api_key: Annotated[str | None, Query()] = None,
176
+ scopes: list[Scope] = [],
177
+ ) -> Verify200Response:
178
+ """
179
+ Verifies the API key and checks if the user scopes are a subset of the API scopes.
180
+ Use this function if you only want to allow access via the API key.
181
+ """
182
+ return await self.api_key_auth(api_key=api_key, scopes=scopes)
183
+
184
+ async def ws_bearer_auth(
185
+ self,
186
+ bearer: Annotated[str | None, Query()] = None,
187
+ scopes: list[Scope] = [],
188
+ ) -> Verify200Response:
189
+ """
190
+ Verifies the bearer token and checks if the user scopes are a subset of the API scopes.
191
+ Use this function if you only want to allow access via the bearer token.
192
+ """
193
+ credentials = (
194
+ HTTPAuthorizationCredentials(scheme="Bearer", credentials=bearer)
195
+ if bearer
196
+ else None
197
+ )
198
+ return await self.bearer_auth(bearer=credentials, scopes=scopes)
199
+
200
+ async def ws_combined_auth(
201
+ self,
202
+ bearer: Annotated[str | None, Query()] = None,
203
+ api_key: Annotated[str | None, Query()] = None,
204
+ scopes: list[Scope] = [],
205
+ ) -> Verify200Response:
206
+ """
207
+ Verifies the bearer token and/or API key and checks if the user scopes are a subset of the API scopes.
208
+ Use this function if you want to allow access via either the bearer token or the API key.
209
+ """
210
+ credentials = (
211
+ HTTPAuthorizationCredentials(scheme="Bearer", credentials=bearer)
212
+ if bearer
213
+ else None
214
+ )
215
+ return await self.combined_auth(
216
+ bearer=credentials, api_key=api_key, scopes=scopes
217
+ )
@@ -1,29 +1,47 @@
1
1
  from enum import Enum
2
2
 
3
3
 
4
- class ApiScope(str, Enum):
4
+ class Scope(str, Enum):
5
5
  """
6
6
  The permission scopes for the API.
7
7
  """
8
8
 
9
+ # If you update anything here, also update the scopes in the auth-service repository
10
+
11
+ @classmethod
12
+ def from_str(cls, value: str) -> "Scope":
13
+ return cls(value)
14
+
9
15
  # Hive scopes
10
- HIVE_MODEL_READ = "hive:model:read"
11
- HIVE_DATA_READ = "hive:data:read"
12
- HIVE_MODEL_WRITE = "hive:model:write"
13
- HIVE_DATA_WRITE = "hive:data:write"
16
+ READ_HIVE_MODEL = "read:hive:model"
17
+ READ_HIVE_DATA = "read:hive:data"
18
+ WRITE_HIVE_MODEL = "write:hive:model"
19
+ WRITE_HIVE_DATA = "write:hive:data"
14
20
 
15
21
  # Trade scopes
16
- TRADE_BOTS_READ = "trade:bots:read"
17
- TRADE_BOTS_WRITE = "trade:bots:write"
18
- TRADE_API_KEYS_READ = "trade:api_keys:read"
19
- TRADE_API_KEYS_WRITE = "trade:api_keys:write"
20
- TRADE_ORDERS_READ = "trade:orders:read"
21
- TRADE_ACTIONS_READ = "trade:actions:read"
22
- TRADE_ACTIONS_WRITE = "trade:actions:write"
23
- TRADE_EXCHANGES_READ = "trade:exchanges:read"
24
- TRADE_FUTURES_READ = "trade:futures:read"
25
- TRADE_FUTURES_WRITE = "trade:futures:write"
26
- TRADE_NOTIFICATIONS_READ = "trade:notifications:read"
27
- TRADE_NOTIFICATIONS_WRITE = "trade:notifications:write"
28
- TRADE_STRATEGIES_READ = "trade:strategies:read"
29
- TRADE_STRATEGIES_WRITE = "trade:strategies:write"
22
+ READ_TRADE_BOTS = "read:trade:bots"
23
+ WRITE_TRADE_BOTS = "write:trade:bots"
24
+ READ_TRADE_API_KEYS = "read:trade:api_keys"
25
+ WRITE_TRADE_API_KEYS = "write:trade:api_keys"
26
+ READ_TRADE_ORDERS = "read:trade:orders"
27
+ READ_TRADE_ACTIONS = "read:trade:actions"
28
+ WRITE_TRADE_ACTIONS = "write:trade:actions"
29
+ READ_TRADE_EXCHANGES = "read:trade:exchanges"
30
+ READ_TRADE_FUTURES = "read:trade:futures"
31
+ WRITE_TRADE_FUTURES = "write:trade:futures"
32
+ READ_TRADE_NOTIFICATIONS = "read:trade:notifications"
33
+ WRITE_TRADE_NOTIFICATIONS = "write:trade:notifications"
34
+ READ_TRADE_STRATEGIES = "read:trade:strategies"
35
+ WRITE_TRADE_STRATEGIES = "write:trade:strategies"
36
+
37
+ # Payment scopes
38
+ READ_PAY_PAYMENTS = "read:pay:payments"
39
+ READ_PAY_PRODUCTS = "read:pay:products"
40
+ WRITE_PAY_PRODUCTS = "write:pay:products"
41
+ READ_PAY_SUBSCRIPTIONS = "read:pay:subscriptions"
42
+ WRITE_PAY_SUBSCRIPTIONS = "write:pay:subscriptions"
43
+ READ_PAY_NOW = "read:pay:now"
44
+ WRITE_PAY_NOW = "write:pay:now"
45
+
46
+ # Read projections
47
+ READ_PREDICTIONS = "read:predictions"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crypticorn
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Maximise Your Crypto Trading Profits with AI Predictions
5
5
  Author-email: Crypticorn <timon@crypticorn.com>
6
6
  Project-URL: Homepage, https://crypticorn.com
@@ -13,16 +13,16 @@ Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Typing :: Typed
14
14
  Requires-Python: >=3.10
15
15
  Description-Content-Type: text/markdown
16
+ Requires-Dist: fastapi
16
17
  Requires-Dist: urllib3<3.0.0,>=1.25.3
17
- Requires-Dist: python_dateutil>=2.8.2
18
- Requires-Dist: aiohttp>=3.8.4
19
- Requires-Dist: aiohttp-retry>=2.8.3
20
- Requires-Dist: pydantic>=2
21
- Requires-Dist: typing-extensions>=4.7.1
18
+ Requires-Dist: python_dateutil<3.0.0,>=2.8.2
19
+ Requires-Dist: aiohttp<4.0.0,>=3.8.4
20
+ Requires-Dist: aiohttp-retry<3.0.0,>=2.8.3
21
+ Requires-Dist: pydantic<3.0.0,>=2.0.0
22
+ Requires-Dist: typing-extensions<5.0.0,>=4.7.1
22
23
  Requires-Dist: pandas<3.0.0,>=2.2.0
23
24
  Requires-Dist: requests<3.0.0,>=2.32.0
24
25
  Requires-Dist: tqdm<5.0.0,>=4.67.0
25
- Requires-Dist: pydantic<3.0.0,>=2.0.0
26
26
  Provides-Extra: dev
27
27
  Requires-Dist: streamlit; extra == "dev"
28
28
  Requires-Dist: httpx; extra == "dev"
@@ -31,12 +31,10 @@ Requires-Dist: black; extra == "dev"
31
31
  Requires-Dist: twine; extra == "dev"
32
32
  Requires-Dist: pyflakes; extra == "dev"
33
33
  Provides-Extra: test
34
- Requires-Dist: pytest>=7.2.1; extra == "test"
35
- Requires-Dist: pytest-cov>=2.8.1; extra == "test"
36
- Requires-Dist: tox>=3.9.0; extra == "test"
37
- Requires-Dist: flake8>=4.0.0; extra == "test"
38
- Requires-Dist: types-python-dateutil>=2.8.19.14; extra == "test"
39
- Requires-Dist: mypy>=1.5; extra == "test"
34
+ Requires-Dist: pytest==8.3.5; extra == "test"
35
+ Requires-Dist: pytest-asyncio==0.26.0; extra == "test"
36
+ Requires-Dist: pytest-cov==6.1.1; extra == "test"
37
+ Requires-Dist: python-dotenv==1.0.1; extra == "test"
40
38
 
41
39
  # What is Crypticorn?
42
40
 
@@ -50,9 +48,28 @@ Crypticorn offers AI-based solutions for both active and passive investors, incl
50
48
  Use this API Client to contribute to the so-called Hive AI, a community driven AI Meta Model for predicting the
51
49
  cryptocurrency market.
52
50
 
51
+ ## Installation
52
+
53
+ You can install the latest stable version from PyPi:
54
+ ```bash
55
+ pip install crypticorn
56
+ ```
57
+
58
+ If you want a specific version, run:
59
+ ```bash
60
+ pip install crypticorn==2.0.0
61
+ ```
62
+
63
+ If you want the latest version, which could be a pre release, run:
64
+ ```bash
65
+ pip install --pre crypticorn
66
+ ```
67
+
53
68
  ## Usage
54
69
 
55
- Within an asynchronous context the session is closed automatically.
70
+ As of know the library is available in async mode only. There are two was of using it.
71
+
72
+ ## With Async Context Protocol
56
73
  ```python
57
74
  async with ApiClient(base_url="http://localhost", jwt=jwt) as client:
58
75
  # json response
@@ -66,6 +83,7 @@ async with ApiClient(base_url="http://localhost", jwt=jwt) as client:
66
83
  print(response)
67
84
  ```
68
85
 
86
+ ## Without Async Context Protocol
69
87
  Without the context you need to close the session manually.
70
88
  ```python
71
89
  client = ApiClient(base_url="http://localhost", jwt=jwt)
@@ -2,7 +2,7 @@ crypticorn/__init__.py,sha256=TL41V09dmtbd2ee07wmOuG9KJJpyvLMPJi5DEd9bDyU,129
2
2
  crypticorn/client.py,sha256=PHtTaV8hWiR-IXcZhQlwEY38q-PhqQjf-JiqaN2MPWg,1844
3
3
  crypticorn/auth/__init__.py,sha256=JAl1tBLK9pYLr_-YKaj581c-c94PWLoqnatTIVAVvMM,81
4
4
  crypticorn/auth/main.py,sha256=ljPuO27VDiOO-jnNycBn96X8UbVHPgOUglj46ib3OjM,1336
5
- crypticorn/auth/client/__init__.py,sha256=PAAEv-J8dFQ9NfwMonLRNVS64t0gozOq45Z_RoWuP98,4694
5
+ crypticorn/auth/client/__init__.py,sha256=yLmQpHhmGXXucIcLSHi8S8QkT5NkQMS5su8u0UWWw6c,5205
6
6
  crypticorn/auth/client/api_client.py,sha256=H2jviD8CKiFnK1ZZXWKbSdBszBYcCQNcga9pwgM03D4,26908
7
7
  crypticorn/auth/client/api_response.py,sha256=WhxwYDSMm6wPixp9CegO8dJzjFxDz3JF1yCq9s0ZqKE,639
8
8
  crypticorn/auth/client/configuration.py,sha256=GNyY6hld-m9Dr7YPngRP5RQGg7o90SsSsSsq7VhyGVs,17715
@@ -11,17 +11,20 @@ crypticorn/auth/client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
11
11
  crypticorn/auth/client/rest.py,sha256=RPbNRRbq7GtH9EBsQxaKT6NtSN8pIp1NmO1SKYZ9vVo,6944
12
12
  crypticorn/auth/client/api/__init__.py,sha256=c-MlPvmOXSsk_ragrcUe8WkOuCPTTpJ8maSp2sER0EU,339
13
13
  crypticorn/auth/client/api/admin_api.py,sha256=Bp52mo4XPDSzVsHolqAykAFpsaioNASY8qDfAUInxgU,21223
14
- crypticorn/auth/client/api/auth_api.py,sha256=72xZo02SGYbxNjOOdfrb4Dqiiqksiqkv1X1B10P41_s,85123
14
+ crypticorn/auth/client/api/auth_api.py,sha256=6kelzbiV3uVxChZ1d53pXW_1COZxoSFfVUJG4_Xx9ug,124292
15
15
  crypticorn/auth/client/api/service_api.py,sha256=xMQKbP9p6fNdJmFa3qMq2RXvE_Brt9HmtprBxEzb0Nk,12423
16
16
  crypticorn/auth/client/api/user_api.py,sha256=nJJ_qD2RYDd6yQ9twHjtwhMJ9lEMSSE91IKltpFC3h0,103457
17
17
  crypticorn/auth/client/api/wallet_api.py,sha256=AF6PSfv5bAnHpThe0Mtz1LD9qhWcd99lMOo7TJDGIt0,68927
18
- crypticorn/auth/client/models/__init__.py,sha256=yrOSQbOq_ZQLDgkjJmmuPxn8vz-AeR2NOEYavs9z1e0,3786
18
+ crypticorn/auth/client/models/__init__.py,sha256=Un7vNZ7LoyUd16FTfU6A8bI_e0i61daLnL65gVvcRjo,4297
19
19
  crypticorn/auth/client/models/add_wallet200_response.py,sha256=ibNUhAku-i2Bglo6ibyQ3V-JlFFauvswYy4doZUNiN4,2559
20
20
  crypticorn/auth/client/models/add_wallet_request.py,sha256=w86tiy1vtlymB7ET0opmENQiEKAGAEjXTC-w4T3LHT4,3204
21
21
  crypticorn/auth/client/models/authorize_user200_response.py,sha256=tkhq7TaQK4li03HKXzrH0GzXa-GIkDVJTUKZ-Qp0USE,3381
22
22
  crypticorn/auth/client/models/authorize_user200_response_auth.py,sha256=h1PFbqzF96ssj7t7dMMKiNtRjRu44kPNUydevJm8vKo,3228
23
23
  crypticorn/auth/client/models/authorize_user_request.py,sha256=VZKmGc91nIhWwoKNTbYZ4oFPYuFNcLxEBvWl-HcM0r8,2981
24
+ crypticorn/auth/client/models/create_api_key200_response.py,sha256=dj6G4vqSTDoUY3jT3_RbUxsLdg_2W-BvfYfwP9O9RJE,2479
25
+ crypticorn/auth/client/models/create_api_key_request.py,sha256=_pnaq1fksWWEXlI0di6ChqBmS7OLOSv4Yke8F9bo6Ao,4947
24
26
  crypticorn/auth/client/models/create_user_request.py,sha256=kqVBJatlPtoYC1-nZnP2Mx2qZP2ATXffod7hTdWtoCY,3501
27
+ crypticorn/auth/client/models/get_api_keys200_response_inner.py,sha256=AGBp8CBwF4UKbn34DjEZ6H33Ys_G3cys0HHTxmJR5RE,5085
25
28
  crypticorn/auth/client/models/list_wallets200_response.py,sha256=OB8nKlBflpj8dXhjTeTewxjSRok3LnllZZ5ZKISDRvE,4752
26
29
  crypticorn/auth/client/models/list_wallets200_response_balances_inner.py,sha256=sLebeWVenEeiHpiCgxQR8iCMlAgjtC6K8CKq6v9g-Ok,4043
27
30
  crypticorn/auth/client/models/list_wallets200_response_balances_inner_sale_round.py,sha256=rqXN7WVEhmeJxDKCSK5sxwWXvmn-Gnf810MGWwIUXII,3623
@@ -31,6 +34,8 @@ crypticorn/auth/client/models/list_wallets200_response_data_inner.py,sha256=IxZr
31
34
  crypticorn/auth/client/models/list_wallets200_response_user_value.py,sha256=e0LbNMdI77IVRt9glwquB6C790o6OWfvitlfyuIoupQ,3841
32
35
  crypticorn/auth/client/models/logout_default_response.py,sha256=ioZ8FrV2DDUdCX2XIkS0X1Y2H0ZjLBCl4_5t_OzrnXE,3395
33
36
  crypticorn/auth/client/models/logout_default_response_issues_inner.py,sha256=_aOp_ngnk19-YLM07nSiqClp6-l6Qa5g6u6Habd1BEc,2487
37
+ crypticorn/auth/client/models/oauth_callback200_response.py,sha256=E7NaQVrrlPtkkaFHG-QNJCQWQzFGXfpjotd2sVZ-gE4,3436
38
+ crypticorn/auth/client/models/oauth_callback200_response_user.py,sha256=ABTvVFLHPR8jMOOHJDvy4_fIVLKuL753ImHLifNQzVg,3018
34
39
  crypticorn/auth/client/models/refresh_token_info200_response.py,sha256=YZITc7MeCrKIVewQxtwOv9xcr26fFhwhnl6o59Ry024,3082
35
40
  crypticorn/auth/client/models/refresh_token_info200_response_user_session.py,sha256=QUz7M-lgSDdvLg2RqFYJkTEJuZtvKlD1CB5BfXo4kFo,3101
36
41
  crypticorn/auth/client/models/resend_verification_email_request.py,sha256=Gx5OONaDxeXaXU7Q30NLMIAvX1XkzmdqbuC6EPlxumk,2546
@@ -48,12 +53,12 @@ crypticorn/auth/client/models/verify_email200_response_auth_auth.py,sha256=bc819
48
53
  crypticorn/auth/client/models/verify_email_request.py,sha256=8MBfxPTLn5X6Z3vE2blxmOtqDhU9tu7O7AyaxkHBG6w,2464
49
54
  crypticorn/auth/client/models/verify_wallet_request.py,sha256=b0DAocvhKzPXPjM62DZqezlHxq3cNL7UVKl0d2judHQ,2691
50
55
  crypticorn/auth/client/models/wallet_verified200_response.py,sha256=QILnTLsCKdI-WdV_fsLBy1UH4ZZU-U-wWJ9ot8v08tI,2465
51
- crypticorn/auth/client/models/whoami200_response.py,sha256=uehdq5epgeOphhrIR3tbrseflxcLAzGyKF-VW-o5cY8,2974
56
+ crypticorn/auth/client/models/whoami200_response.py,sha256=JhJAuL_N2yXGt5gfX4Dx46isuFfcNmpi2Qw7z0UJpMU,3086
52
57
  crypticorn/common/__init__.py,sha256=lY87VMTkIEqto6kcEjC1YWsUvT03QuPmXwxCaeWE854,196
53
- crypticorn/common/auth.py,sha256=pqmn5wG9CWql7wwAOs3p8zWlR2XyOzcoA17xgC6KsjA,1213
54
- crypticorn/common/auth_client.py,sha256=PlMBSQ_SyWGvZQsc97DlokoWLprwlo0r9-oftD89Rps,5564
58
+ crypticorn/common/auth.py,sha256=kO03VKuFRIoWXMDLPmgnVZ49EeTqOMzgJ2yEXt_SmQM,1223
59
+ crypticorn/common/auth_client.py,sha256=7ptCkWZwuMSIFTx3gajOH4CScPFG-Pn5gLe1m_NY5AQ,7622
55
60
  crypticorn/common/errors.py,sha256=jGAS7TONKhdaRfft7w-0wrJ3BBTzqS6u03susiqAF0w,12723
56
- crypticorn/common/scopes.py,sha256=60dLff6zADjkKLwsOrwfpgFoCQjh3fqBS5Mpe5PonaU,991
61
+ crypticorn/common/scopes.py,sha256=E3P8JeoziZjEv-nef1PakjowjmhIebkvIeLYD_NRY0Q,1570
57
62
  crypticorn/common/urls.py,sha256=_NMhvhZXOsZpDBbgucqu0yboRFox6JVMlOoQq_Y5SGA,432
58
63
  crypticorn/hive/__init__.py,sha256=hRfTlEzEql4msytdUC_04vfaHzVKG5CGZle1M-9QFgY,81
59
64
  crypticorn/hive/main.py,sha256=RmCYSR0jwmfYWTK89dt79DuGPjEaip9XQs_LWNWr_tc,1036
@@ -220,7 +225,7 @@ crypticorn/trade/client/models/trading_action_type.py,sha256=jW0OsNz_ZNXlITxAfh9
220
225
  crypticorn/trade/client/models/update_notification.py,sha256=B9QUuVRNpk1e5G8o0WFgIg3inm-OX7KJAJcjVnRzYx8,3046
221
226
  crypticorn/trade/client/models/validation_error.py,sha256=uTkvsKrOAt-21UC0YPqCdRl_OMsuu7uhPtWuwRSYvv0,3228
222
227
  crypticorn/trade/client/models/validation_error_loc_inner.py,sha256=22ql-H829xTBgfxNQZsqd8fS3zQt9tLW1pj0iobo0jY,5131
223
- crypticorn-2.0.0.dist-info/METADATA,sha256=WEAVy4EuJjyFyULElXpwnA3eyiqt5yeX7eGrwk_HVI4,2804
224
- crypticorn-2.0.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
225
- crypticorn-2.0.0.dist-info/top_level.txt,sha256=EP3NY216qIBYfmvGl0L2Zc9ItP0DjGSkiYqd9xJwGcM,11
226
- crypticorn-2.0.0.dist-info/RECORD,,
228
+ crypticorn-2.1.0.dist-info/METADATA,sha256=oQ5ZEl55q9O3z1_EjUSlwGSxepxwaTANO8RAVjzj_ZI,3120
229
+ crypticorn-2.1.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
230
+ crypticorn-2.1.0.dist-info/top_level.txt,sha256=EP3NY216qIBYfmvGl0L2Zc9ItP0DjGSkiYqd9xJwGcM,11
231
+ crypticorn-2.1.0.dist-info/RECORD,,