silhouette-python-sdk 0.2.0__tar.gz → 0.3.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.
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/PKG-INFO +42 -15
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/README.md +39 -12
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/pyproject.toml +18 -10
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/api/__init__.py +6 -1
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/api/auth.py +43 -45
- silhouette_python_sdk-0.3.0/silhouette/api/base_model.py +11 -0
- silhouette_python_sdk-0.3.0/silhouette/api/client.py +1330 -0
- silhouette_python_sdk-0.3.0/silhouette/api/generated/__init__.py +2268 -0
- silhouette_python_sdk-0.3.0/silhouette/api/rfq_types.py +15 -0
- silhouette_python_sdk-0.3.0/silhouette/api/v1/__init__.py +1 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/__init__.py +1 -1
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/exchange.py +5 -13
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/twap/USAGE_GUIDE.md +4 -8
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/twap/__init__.py +1 -6
- silhouette_python_sdk-0.3.0/silhouette/twap/authenticated_client.py +114 -0
- silhouette_python_sdk-0.3.0/silhouette/twap/base_model.py +11 -0
- silhouette_python_sdk-0.3.0/silhouette/twap/client.py +171 -0
- silhouette_python_sdk-0.3.0/silhouette/twap/generated/__init__.py +56 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/twap/interfaces.py +8 -27
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/utils/constants.py +1 -1
- silhouette_python_sdk-0.2.0/generated/.gitkeep +0 -3
- silhouette_python_sdk-0.2.0/generated/__init__.py +0 -1
- silhouette_python_sdk-0.2.0/generated/twap_client/.gitignore +0 -23
- silhouette_python_sdk-0.2.0/generated/twap_client/README.md +0 -124
- silhouette_python_sdk-0.2.0/generated/twap_client/pyproject.toml +0 -26
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/__init__.py +0 -8
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/__init__.py +0 -1
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/auth/__init__.py +0 -1
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/auth/auth_controller_register_v_0.py +0 -156
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/auth/auth_controller_verify_v_0.py +0 -123
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/hyperliquid/__init__.py +0 -1
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/hyperliquid/hyperliquid_controller_get_exchange_status_v_0.py +0 -125
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/hyperliquid/hyperliquid_controller_l_2_book_v_0.py +0 -123
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/orders/__init__.py +0 -1
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/orders/orders_controller_cancel_order_v_0.py +0 -147
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/orders/orders_controller_create_twap_order_v_0.py +0 -156
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/orders/orders_controller_get_orders_by_user_v_0.py +0 -130
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/orders/orders_controller_get_task_status_v_0.py +0 -147
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/orders/orders_controller_get_twap_order_v_0.py +0 -147
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/orders/orders_controller_pause_order_v_0.py +0 -141
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/orders/orders_controller_resume_order_v_0.py +0 -141
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/system/__init__.py +0 -1
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/api/system/system_controller_get_health_check_v_0.py +0 -123
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/client.py +0 -268
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/errors.py +0 -16
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/__init__.py +0 -37
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/auth_controller_register_v0_response_200.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/auth_controller_verify_v0_response_200.py +0 -77
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/create_twap_order_dto.py +0 -175
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/create_twap_order_dto_market_type.py +0 -9
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/create_twap_order_dto_order_intent.py +0 -9
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/create_twap_order_dto_order_side.py +0 -9
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/hyperliquid_controller_get_exchange_status_v0_response_200.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/hyperliquid_controller_l2_book_v0_response_200.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/orders_controller_cancel_order_v0_response_200.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/orders_controller_create_twap_order_v0_response_201.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/orders_controller_get_orders_by_user_v0_response_200_item.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/orders_controller_get_task_status_v0_response_200.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/orders_controller_get_twap_order_v0_response_200.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/register_api_key_dto.py +0 -67
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/models/system_controller_get_health_check_v0_response_200.py +0 -44
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/py.typed +0 -1
- silhouette_python_sdk-0.2.0/generated/twap_client/silhouette_scheduler_api_reference_client/types.py +0 -54
- silhouette_python_sdk-0.2.0/silhouette/abi/silhouette.json +0 -1
- silhouette_python_sdk-0.2.0/silhouette/api/client.py +0 -695
- silhouette_python_sdk-0.2.0/silhouette/api/generated/__init__.py +0 -118
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/__init__.py +0 -122
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/api_error.py +0 -97
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/auth_required_event.py +0 -87
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/balance_event.py +0 -99
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/batch_get_delegated_orders_request.py +0 -93
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/batch_get_delegated_orders_response.py +0 -102
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/cancel_order_request.py +0 -94
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/cancel_order_response.py +0 -102
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/create_order_request.py +0 -152
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/create_order_response.py +0 -102
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/events_get401_response.py +0 -85
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/events_get429_response.py +0 -85
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/events_get503_response.py +0 -85
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_balances_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_balances_response.py +0 -108
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_balances_response_balances_inner.py +0 -97
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_delegated_order_request.py +0 -93
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_delegated_order_response.py +0 -100
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_health_features_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_health_features_response.py +0 -98
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_health_info_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_health_info_response.py +0 -98
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_health_ready_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_health_ready_response.py +0 -98
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_health_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_health_response.py +0 -100
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_history_request.py +0 -97
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_history_response.py +0 -98
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_hypercore_trading_info_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_hypercore_trading_info_response.py +0 -98
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_l1_balances_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_l1_balances_response.py +0 -98
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_stats_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_stats_response.py +0 -98
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_user_orders_request.py +0 -104
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_user_orders_response.py +0 -108
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_user_orders_response_orders_inner.py +0 -153
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_user_withdrawals_request.py +0 -92
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_user_withdrawals_response.py +0 -108
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_withdrawal_status_request.py +0 -94
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_withdrawal_status_response.py +0 -104
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/get_withdrawal_status_response_withdrawal.py +0 -118
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/heartbeat_event.py +0 -85
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/initiate_withdrawal_request.py +0 -111
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/initiate_withdrawal_response.py +0 -102
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/list_delegated_orders_request.py +0 -97
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/list_delegated_orders_response.py +0 -102
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/login_request.py +0 -96
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/login_response.py +0 -100
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/match_event.py +0 -107
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/order_event.py +0 -138
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/ping_get200_response.py +0 -97
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/refund_event.py +0 -102
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/response_metadata.py +0 -98
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/v0_get200_response.py +0 -91
- silhouette_python_sdk-0.2.0/silhouette/api/generated/models/withdrawal_event.py +0 -112
- silhouette_python_sdk-0.2.0/silhouette/twap/auth/__init__.py +0 -41
- silhouette_python_sdk-0.2.0/silhouette/twap/auth/exceptions.py +0 -160
- silhouette_python_sdk-0.2.0/silhouette/twap/auth/jwt_service.py +0 -330
- silhouette_python_sdk-0.2.0/silhouette/twap/auth/jwt_signer.py +0 -215
- silhouette_python_sdk-0.2.0/silhouette/twap/auth/utils.py +0 -125
- silhouette_python_sdk-0.2.0/silhouette/twap/authenticated_client.py +0 -320
- silhouette_python_sdk-0.2.0/silhouette/twap/client.py +0 -1024
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/LICENSE.md +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/__init__.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/api.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/info.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/utils/__init__.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/utils/constants.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/utils/error.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/utils/signing.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/utils/types.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/hyperliquid/websocket_manager.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/py.typed +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/utils/__init__.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/utils/conversions.py +0 -0
- {silhouette_python_sdk-0.2.0 → silhouette_python_sdk-0.3.0}/silhouette/utils/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: silhouette-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Python SDK for trading on Silhouette - the shield exchange on Hyperliquid
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: silhouette,trading,privacy,exchange,hyperliquid,defi,crypto,sdk
|
|
@@ -17,15 +17,15 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
-
Requires-Dist: attrs (>=22.2)
|
|
21
20
|
Requires-Dist: cryptography (>=46,<47)
|
|
22
21
|
Requires-Dist: httpx (>=0.23,<0.29)
|
|
23
22
|
Requires-Dist: hyperliquid-python-sdk (>=0.21,<1)
|
|
23
|
+
Requires-Dist: pydantic (>=2.0,<3)
|
|
24
24
|
Requires-Dist: pyjwt (>=2.10,<3)
|
|
25
|
-
Requires-Dist: python-dateutil (>=2.8,<3)
|
|
26
25
|
Requires-Dist: requests (>=2.32,<3)
|
|
27
26
|
Requires-Dist: siwe (>=4.4,<5)
|
|
28
27
|
Requires-Dist: typing_extensions (>=4.9) ; python_version < "3.11"
|
|
28
|
+
Requires-Dist: websockets (>=15,<16)
|
|
29
29
|
Project-URL: Homepage, https://github.com/silhouette-exchange/silhouette-python-sdk
|
|
30
30
|
Project-URL: Repository, https://github.com/silhouette-exchange/silhouette-python-sdk
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
@@ -105,9 +105,9 @@ print(f"Balances: {balances_response['balances']}")
|
|
|
105
105
|
# Place an order (returns dict, no need to import models)
|
|
106
106
|
order = client.order.create_order(
|
|
107
107
|
side="buy",
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
order_type="limit",
|
|
109
|
+
base_token="HYPE",
|
|
110
|
+
quote_token="USDC",
|
|
111
111
|
amount="1.0",
|
|
112
112
|
price="0.001",
|
|
113
113
|
)
|
|
@@ -129,7 +129,7 @@ The enhanced Hyperliquid SDK includes these convenience methods:
|
|
|
129
129
|
- `await_withdrawal_completion(wallet_address: str, pre_withdrawal_balance: float, token_symbol: str, timeout: int) -> bool` - Poll balance until withdrawal completes
|
|
130
130
|
|
|
131
131
|
**Exchange class:**
|
|
132
|
-
- `deposit_to_silhouette(
|
|
132
|
+
- `deposit_to_silhouette(silhouette_address: str, token_symbol: str, amount: str, converter: TokenConverter) -> dict` - Deposit tokens from Hyperliquid to Silhouette contract
|
|
133
133
|
|
|
134
134
|
All other Hyperliquid SDK methods work exactly as documented in the [official Hyperliquid SDK](https://github.com/hyperliquid-dex/hyperliquid-python-sdk).
|
|
135
135
|
|
|
@@ -141,6 +141,7 @@ The `SilhouetteApiClient` provides access to the Silhouette shielded exchange:
|
|
|
141
141
|
- **No model imports needed**: Pydantic models are used internally for validation, but you work with simple dicts
|
|
142
142
|
- **User operations**: Balances, withdrawal initiation
|
|
143
143
|
- **Order management**: Create, cancel, query orders
|
|
144
|
+
- **Delegated orders**: List, get, and batch retrieve delegated orders
|
|
144
145
|
- **Trade execution**: Privacy-preserving order execution
|
|
145
146
|
- **Automatic authentication**: SIWE-based session management with automatic token refresh
|
|
146
147
|
|
|
@@ -154,6 +155,29 @@ response = client.user.get_balances()
|
|
|
154
155
|
# }
|
|
155
156
|
```
|
|
156
157
|
|
|
158
|
+
### Delegated orders
|
|
159
|
+
|
|
160
|
+
Delegated orders are orders placed on Hyperliquid by Silhouette on behalf of a trader. Query them using the `delegated_order` operations:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# List delegated orders with optional filters
|
|
164
|
+
orders = client.delegated_order.list_delegated_orders(
|
|
165
|
+
status="open",
|
|
166
|
+
limit=50,
|
|
167
|
+
)
|
|
168
|
+
print(f"Found {len(orders['orders'])} orders")
|
|
169
|
+
|
|
170
|
+
# Get a single order by ID
|
|
171
|
+
order = client.delegated_order.get_delegated_order("order-id-here")
|
|
172
|
+
print(f"Order status: {order['order']['status']}")
|
|
173
|
+
|
|
174
|
+
# Batch retrieve multiple orders
|
|
175
|
+
batch = client.delegated_order.batch_get_delegated_orders(
|
|
176
|
+
["order-1", "order-2", "order-3"]
|
|
177
|
+
)
|
|
178
|
+
print(f"Found: {len(batch['orders'])}, Not found: {batch['notFound']}")
|
|
179
|
+
```
|
|
180
|
+
|
|
157
181
|
## API Design
|
|
158
182
|
|
|
159
183
|
### Why Dicts Instead of Models?
|
|
@@ -173,21 +197,24 @@ The SDK uses a hybrid approach for optimal developer experience:
|
|
|
173
197
|
|
|
174
198
|
### Regenerating models
|
|
175
199
|
|
|
176
|
-
Models are generated from the OpenAPI
|
|
200
|
+
Models are generated from the OpenAPI specifications using `datamodel-code-generator`. When the Silhouette API changes, regenerate the models to stay in sync:
|
|
177
201
|
|
|
178
202
|
```bash
|
|
179
|
-
#
|
|
180
|
-
|
|
203
|
+
# Generate from committed specs
|
|
204
|
+
make generate
|
|
181
205
|
|
|
182
|
-
# Or
|
|
183
|
-
|
|
206
|
+
# Or fetch fresh specs and regenerate in one step
|
|
207
|
+
make regenerate
|
|
184
208
|
|
|
185
|
-
#
|
|
209
|
+
# Fetch from a specific URL
|
|
210
|
+
make fetch-heimdall-v0-spec URL=https://api-staging.silhouette.exchange
|
|
211
|
+
make fetch-heimdall-v1-spec URL=https://api-staging.silhouette.exchange
|
|
212
|
+
make generate
|
|
213
|
+
|
|
214
|
+
# Run tests to catch any breaking changes
|
|
186
215
|
make test
|
|
187
216
|
```
|
|
188
217
|
|
|
189
|
-
Requires `curl`, `jq`, and `openapi-generator-cli` to be installed.
|
|
190
|
-
|
|
191
218
|
## Configuration
|
|
192
219
|
|
|
193
220
|
Create `examples/config.json` from the example template:
|
|
@@ -73,9 +73,9 @@ print(f"Balances: {balances_response['balances']}")
|
|
|
73
73
|
# Place an order (returns dict, no need to import models)
|
|
74
74
|
order = client.order.create_order(
|
|
75
75
|
side="buy",
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
order_type="limit",
|
|
77
|
+
base_token="HYPE",
|
|
78
|
+
quote_token="USDC",
|
|
79
79
|
amount="1.0",
|
|
80
80
|
price="0.001",
|
|
81
81
|
)
|
|
@@ -97,7 +97,7 @@ The enhanced Hyperliquid SDK includes these convenience methods:
|
|
|
97
97
|
- `await_withdrawal_completion(wallet_address: str, pre_withdrawal_balance: float, token_symbol: str, timeout: int) -> bool` - Poll balance until withdrawal completes
|
|
98
98
|
|
|
99
99
|
**Exchange class:**
|
|
100
|
-
- `deposit_to_silhouette(
|
|
100
|
+
- `deposit_to_silhouette(silhouette_address: str, token_symbol: str, amount: str, converter: TokenConverter) -> dict` - Deposit tokens from Hyperliquid to Silhouette contract
|
|
101
101
|
|
|
102
102
|
All other Hyperliquid SDK methods work exactly as documented in the [official Hyperliquid SDK](https://github.com/hyperliquid-dex/hyperliquid-python-sdk).
|
|
103
103
|
|
|
@@ -109,6 +109,7 @@ The `SilhouetteApiClient` provides access to the Silhouette shielded exchange:
|
|
|
109
109
|
- **No model imports needed**: Pydantic models are used internally for validation, but you work with simple dicts
|
|
110
110
|
- **User operations**: Balances, withdrawal initiation
|
|
111
111
|
- **Order management**: Create, cancel, query orders
|
|
112
|
+
- **Delegated orders**: List, get, and batch retrieve delegated orders
|
|
112
113
|
- **Trade execution**: Privacy-preserving order execution
|
|
113
114
|
- **Automatic authentication**: SIWE-based session management with automatic token refresh
|
|
114
115
|
|
|
@@ -122,6 +123,29 @@ response = client.user.get_balances()
|
|
|
122
123
|
# }
|
|
123
124
|
```
|
|
124
125
|
|
|
126
|
+
### Delegated orders
|
|
127
|
+
|
|
128
|
+
Delegated orders are orders placed on Hyperliquid by Silhouette on behalf of a trader. Query them using the `delegated_order` operations:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
# List delegated orders with optional filters
|
|
132
|
+
orders = client.delegated_order.list_delegated_orders(
|
|
133
|
+
status="open",
|
|
134
|
+
limit=50,
|
|
135
|
+
)
|
|
136
|
+
print(f"Found {len(orders['orders'])} orders")
|
|
137
|
+
|
|
138
|
+
# Get a single order by ID
|
|
139
|
+
order = client.delegated_order.get_delegated_order("order-id-here")
|
|
140
|
+
print(f"Order status: {order['order']['status']}")
|
|
141
|
+
|
|
142
|
+
# Batch retrieve multiple orders
|
|
143
|
+
batch = client.delegated_order.batch_get_delegated_orders(
|
|
144
|
+
["order-1", "order-2", "order-3"]
|
|
145
|
+
)
|
|
146
|
+
print(f"Found: {len(batch['orders'])}, Not found: {batch['notFound']}")
|
|
147
|
+
```
|
|
148
|
+
|
|
125
149
|
## API Design
|
|
126
150
|
|
|
127
151
|
### Why Dicts Instead of Models?
|
|
@@ -141,21 +165,24 @@ The SDK uses a hybrid approach for optimal developer experience:
|
|
|
141
165
|
|
|
142
166
|
### Regenerating models
|
|
143
167
|
|
|
144
|
-
Models are generated from the OpenAPI
|
|
168
|
+
Models are generated from the OpenAPI specifications using `datamodel-code-generator`. When the Silhouette API changes, regenerate the models to stay in sync:
|
|
145
169
|
|
|
146
170
|
```bash
|
|
147
|
-
#
|
|
148
|
-
|
|
171
|
+
# Generate from committed specs
|
|
172
|
+
make generate
|
|
149
173
|
|
|
150
|
-
# Or
|
|
151
|
-
|
|
174
|
+
# Or fetch fresh specs and regenerate in one step
|
|
175
|
+
make regenerate
|
|
152
176
|
|
|
153
|
-
#
|
|
177
|
+
# Fetch from a specific URL
|
|
178
|
+
make fetch-heimdall-v0-spec URL=https://api-staging.silhouette.exchange
|
|
179
|
+
make fetch-heimdall-v1-spec URL=https://api-staging.silhouette.exchange
|
|
180
|
+
make generate
|
|
181
|
+
|
|
182
|
+
# Run tests to catch any breaking changes
|
|
154
183
|
make test
|
|
155
184
|
```
|
|
156
185
|
|
|
157
|
-
Requires `curl`, `jq`, and `openapi-generator-cli` to be installed.
|
|
158
|
-
|
|
159
186
|
## Configuration
|
|
160
187
|
|
|
161
188
|
Create `examples/config.json` from the example template:
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "silhouette-python-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Python SDK for trading on Silhouette - the shield exchange on Hyperliquid"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -31,8 +31,8 @@ dependencies = [
|
|
|
31
31
|
"pyjwt>=2.10,<3",
|
|
32
32
|
"cryptography>=46,<47",
|
|
33
33
|
"httpx>=0.23,<0.29",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"websockets>=15,<16",
|
|
35
|
+
"pydantic>=2.0,<3",
|
|
36
36
|
]
|
|
37
37
|
|
|
38
38
|
[project.urls]
|
|
@@ -42,9 +42,13 @@ Repository = "https://github.com/silhouette-exchange/silhouette-python-sdk"
|
|
|
42
42
|
[tool.poetry]
|
|
43
43
|
packages = [
|
|
44
44
|
{ include = "silhouette" },
|
|
45
|
-
{ include = "generated" },
|
|
46
45
|
]
|
|
47
|
-
include = [
|
|
46
|
+
include = [
|
|
47
|
+
"silhouette/py.typed",
|
|
48
|
+
{ path = "silhouette/api/generated/__init__.py", format = ["sdist", "wheel"] },
|
|
49
|
+
{ path = "silhouette/api/v1/generated/__init__.py", format = ["sdist", "wheel"] },
|
|
50
|
+
{ path = "silhouette/twap/generated/__init__.py", format = ["sdist", "wheel"] },
|
|
51
|
+
]
|
|
48
52
|
|
|
49
53
|
[tool.poetry.group.dev.dependencies]
|
|
50
54
|
pytest = "^8.4"
|
|
@@ -54,15 +58,14 @@ pre-commit = "^4.3"
|
|
|
54
58
|
safety = "^3.6"
|
|
55
59
|
coverage = "^7.10"
|
|
56
60
|
pytest-cov = "^7"
|
|
57
|
-
pytest-socket = "^0.7"
|
|
61
|
+
pytest-socket = "^0.7.0"
|
|
58
62
|
types-requests = "^2.31"
|
|
59
|
-
|
|
60
|
-
openapi-python-client = "^0.26"
|
|
63
|
+
datamodel-code-generator = {version = "^0.29", extras = ["http"]}
|
|
61
64
|
|
|
62
65
|
[tool.ruff]
|
|
63
66
|
target-version = "py310"
|
|
64
67
|
line-length = 120
|
|
65
|
-
exclude = [".git", ".mypy_cache", ".ruff_cache", "__pycache__", "dist", "build", "silhouette/api/generated"]
|
|
68
|
+
exclude = [".git", ".mypy_cache", ".ruff_cache", "__pycache__", "dist", "build", "silhouette/api/generated", "silhouette/api/v1/generated", "silhouette/twap/generated"]
|
|
66
69
|
|
|
67
70
|
[tool.ruff.lint]
|
|
68
71
|
select = ["E", "W", "F", "I", "B", "C4", "UP", "S"]
|
|
@@ -94,7 +97,7 @@ python_version = "3.10"
|
|
|
94
97
|
pretty = true
|
|
95
98
|
show_traceback = true
|
|
96
99
|
color_output = true
|
|
97
|
-
exclude = ["examples/", "silhouette/api/generated/"]
|
|
100
|
+
exclude = ["examples/", "silhouette/api/generated/", "silhouette/api/v1/generated/", "silhouette/twap/generated/"]
|
|
98
101
|
|
|
99
102
|
allow_redefinition = false
|
|
100
103
|
check_untyped_defs = true
|
|
@@ -162,7 +165,10 @@ norecursedirs = [
|
|
|
162
165
|
".tox",
|
|
163
166
|
".git",
|
|
164
167
|
"__pycache__",
|
|
168
|
+
"examples",
|
|
165
169
|
"silhouette/api/generated",
|
|
170
|
+
"silhouette/api/v1/generated",
|
|
171
|
+
"silhouette/twap/generated",
|
|
166
172
|
]
|
|
167
173
|
doctest_optionflags = [
|
|
168
174
|
"NUMBER",
|
|
@@ -186,6 +192,8 @@ addopts = [
|
|
|
186
192
|
branch = true
|
|
187
193
|
omit = [
|
|
188
194
|
"silhouette/api/generated/*",
|
|
195
|
+
"silhouette/api/v1/generated/*",
|
|
196
|
+
"silhouette/twap/generated/*",
|
|
189
197
|
]
|
|
190
198
|
|
|
191
199
|
[tool.coverage.report]
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"""Silhouette API client for enclave integration."""
|
|
2
2
|
|
|
3
3
|
from silhouette.api.client import SilhouetteApiClient, SilhouetteApiError
|
|
4
|
+
from silhouette.api.rfq_types import RfqEvent
|
|
4
5
|
|
|
5
|
-
__all__ = [
|
|
6
|
+
__all__ = [
|
|
7
|
+
"RfqEvent",
|
|
8
|
+
"SilhouetteApiClient",
|
|
9
|
+
"SilhouetteApiError",
|
|
10
|
+
]
|
|
@@ -40,6 +40,44 @@ class AuthConfig:
|
|
|
40
40
|
"""Chain ID for SIWE message signing (1 for mainnet, 421614 for Arbitrum Sepolia testnet)."""
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def sign_siwe_message(
|
|
44
|
+
wallet: LocalAccount,
|
|
45
|
+
base_url: str,
|
|
46
|
+
chain_id: int = 1,
|
|
47
|
+
) -> tuple[str, str]:
|
|
48
|
+
"""Create and sign a SIWE message for authentication.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
wallet: The wallet to sign with
|
|
52
|
+
base_url: The API base URL (used for domain and URI)
|
|
53
|
+
chain_id: Chain ID for SIWE message (1 for mainnet)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Tuple of (prepared_message_str, signature_hex)
|
|
57
|
+
"""
|
|
58
|
+
from urllib.parse import urlparse
|
|
59
|
+
|
|
60
|
+
parsed = urlparse(base_url)
|
|
61
|
+
domain = parsed.netloc or "localhost"
|
|
62
|
+
|
|
63
|
+
message = SiweMessage(
|
|
64
|
+
domain=domain,
|
|
65
|
+
address=wallet.address,
|
|
66
|
+
statement="Sign in with Ethereum to the app.",
|
|
67
|
+
uri=f"{base_url}/login",
|
|
68
|
+
version="1",
|
|
69
|
+
chain_id=chain_id,
|
|
70
|
+
nonce=secrets.token_hex(12),
|
|
71
|
+
issued_at=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
72
|
+
)
|
|
73
|
+
prepared_message_str = message.prepare_message()
|
|
74
|
+
|
|
75
|
+
message_to_sign = encode_defunct(text=prepared_message_str)
|
|
76
|
+
signed_message = wallet.sign_message(message_to_sign)
|
|
77
|
+
signature_hex = "0x" + signed_message.signature.hex()
|
|
78
|
+
return prepared_message_str, signature_hex
|
|
79
|
+
|
|
80
|
+
|
|
43
81
|
class AuthManager:
|
|
44
82
|
"""Manages authentication state and automatic login for the API client."""
|
|
45
83
|
|
|
@@ -191,26 +229,6 @@ class AuthManager:
|
|
|
191
229
|
|
|
192
230
|
raise RuntimeError(f"Login failed after {self.config.max_login_retries} attempts: {last_error}") from last_error
|
|
193
231
|
|
|
194
|
-
def _extract_domain_from_url(self, url: str) -> str:
|
|
195
|
-
"""
|
|
196
|
-
Extract domain (host:port) from a URL for SIWE message.
|
|
197
|
-
|
|
198
|
-
Args:
|
|
199
|
-
url: Full URL like "https://api.silhouette.exchange/v0"
|
|
200
|
-
|
|
201
|
-
Returns:
|
|
202
|
-
Domain string like "api.silhouette.exchange""
|
|
203
|
-
"""
|
|
204
|
-
from urllib.parse import urlparse
|
|
205
|
-
|
|
206
|
-
parsed = urlparse(url)
|
|
207
|
-
# netloc includes host and port (e.g., "localhost:8081" or "api.silhouette.exchange")
|
|
208
|
-
domain = parsed.netloc
|
|
209
|
-
if not domain:
|
|
210
|
-
# Fallback if URL parsing fails
|
|
211
|
-
domain = "localhost"
|
|
212
|
-
return domain
|
|
213
|
-
|
|
214
232
|
def _perform_login(self) -> None:
|
|
215
233
|
"""
|
|
216
234
|
Perform the SIWE login flow.
|
|
@@ -221,32 +239,12 @@ class AuthManager:
|
|
|
221
239
|
if self._wallet is None:
|
|
222
240
|
raise ValueError("No wallet configured for login")
|
|
223
241
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
message = SiweMessage(
|
|
229
|
-
domain=domain,
|
|
230
|
-
address=self._wallet.address,
|
|
231
|
-
statement="Sign in with Ethereum to the app.",
|
|
232
|
-
uri=f"{self.client.base_url}/login",
|
|
233
|
-
version="1",
|
|
234
|
-
chain_id=self.config.chain_id,
|
|
235
|
-
nonce=secrets.token_hex(12),
|
|
236
|
-
issued_at=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
242
|
+
message, signature = sign_siwe_message(
|
|
243
|
+
self._wallet,
|
|
244
|
+
self.client.base_url,
|
|
245
|
+
self.config.chain_id,
|
|
237
246
|
)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
# Sign the message
|
|
241
|
-
message_to_sign = encode_defunct(text=prepared_message_str)
|
|
242
|
-
signed_message = self._wallet.sign_message(message_to_sign)
|
|
243
|
-
|
|
244
|
-
# Verify signature client-side
|
|
245
|
-
message.verify(signed_message.signature)
|
|
246
|
-
|
|
247
|
-
# Call login API
|
|
248
|
-
signature_hex = "0x" + signed_message.signature.hex()
|
|
249
|
-
token = self.client._raw_login(prepared_message_str, signature_hex)
|
|
247
|
+
token = self.client._raw_login(message, signature)
|
|
250
248
|
self.set_token(token)
|
|
251
249
|
|
|
252
250
|
def handle_auth_error(self, response_status: int) -> bool:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Base model for generated Pydantic models.
|
|
2
|
+
|
|
3
|
+
Configures `populate_by_name=True` so that models accept both snake_case
|
|
4
|
+
field names and their camelCase aliases when constructing instances.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ApiBaseModel(BaseModel):
|
|
11
|
+
model_config = ConfigDict(populate_by_name=True)
|