unicex 0.13.17__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.
- unicex/__init__.py +200 -0
- unicex/_abc/__init__.py +11 -0
- unicex/_abc/exchange_info.py +216 -0
- unicex/_abc/uni_client.py +329 -0
- unicex/_abc/uni_websocket_manager.py +294 -0
- unicex/_base/__init__.py +9 -0
- unicex/_base/client.py +214 -0
- unicex/_base/websocket.py +261 -0
- unicex/binance/__init__.py +27 -0
- unicex/binance/adapter.py +202 -0
- unicex/binance/client.py +1577 -0
- unicex/binance/exchange_info.py +62 -0
- unicex/binance/uni_client.py +188 -0
- unicex/binance/uni_websocket_manager.py +166 -0
- unicex/binance/user_websocket.py +186 -0
- unicex/binance/websocket_manager.py +912 -0
- unicex/bitget/__init__.py +27 -0
- unicex/bitget/adapter.py +188 -0
- unicex/bitget/client.py +2514 -0
- unicex/bitget/exchange_info.py +48 -0
- unicex/bitget/uni_client.py +198 -0
- unicex/bitget/uni_websocket_manager.py +275 -0
- unicex/bitget/user_websocket.py +7 -0
- unicex/bitget/websocket_manager.py +232 -0
- unicex/bybit/__init__.py +27 -0
- unicex/bybit/adapter.py +208 -0
- unicex/bybit/client.py +1876 -0
- unicex/bybit/exchange_info.py +53 -0
- unicex/bybit/uni_client.py +200 -0
- unicex/bybit/uni_websocket_manager.py +291 -0
- unicex/bybit/user_websocket.py +7 -0
- unicex/bybit/websocket_manager.py +339 -0
- unicex/enums.py +273 -0
- unicex/exceptions.py +64 -0
- unicex/extra.py +335 -0
- unicex/gate/__init__.py +27 -0
- unicex/gate/adapter.py +178 -0
- unicex/gate/client.py +1667 -0
- unicex/gate/exchange_info.py +55 -0
- unicex/gate/uni_client.py +214 -0
- unicex/gate/uni_websocket_manager.py +269 -0
- unicex/gate/user_websocket.py +7 -0
- unicex/gate/websocket_manager.py +513 -0
- unicex/hyperliquid/__init__.py +27 -0
- unicex/hyperliquid/adapter.py +261 -0
- unicex/hyperliquid/client.py +2315 -0
- unicex/hyperliquid/exchange_info.py +119 -0
- unicex/hyperliquid/uni_client.py +325 -0
- unicex/hyperliquid/uni_websocket_manager.py +269 -0
- unicex/hyperliquid/user_websocket.py +7 -0
- unicex/hyperliquid/websocket_manager.py +393 -0
- unicex/mapper.py +111 -0
- unicex/mexc/__init__.py +27 -0
- unicex/mexc/_spot_ws_proto/PrivateAccountV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PrivateDealsV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PrivateOrdersV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicAggreBookTickerV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicAggreDealsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicAggreDepthsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicBookTickerBatchV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicBookTickerV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicDealsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicFuture_pb2.py +103 -0
- unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsBatchV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicLimitDepthsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicMiniTickerV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicMiniTickersV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicSpotKlineV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PushDataV3ApiWrapper_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/__init__.py +335 -0
- unicex/mexc/adapter.py +239 -0
- unicex/mexc/client.py +846 -0
- unicex/mexc/exchange_info.py +47 -0
- unicex/mexc/uni_client.py +211 -0
- unicex/mexc/uni_websocket_manager.py +269 -0
- unicex/mexc/user_websocket.py +7 -0
- unicex/mexc/websocket_manager.py +456 -0
- unicex/okx/__init__.py +27 -0
- unicex/okx/adapter.py +150 -0
- unicex/okx/client.py +2864 -0
- unicex/okx/exchange_info.py +47 -0
- unicex/okx/uni_client.py +202 -0
- unicex/okx/uni_websocket_manager.py +269 -0
- unicex/okx/user_websocket.py +7 -0
- unicex/okx/websocket_manager.py +743 -0
- unicex/types.py +164 -0
- unicex/utils.py +218 -0
- unicex-0.13.17.dist-info/METADATA +243 -0
- unicex-0.13.17.dist-info/RECORD +93 -0
- unicex-0.13.17.dist-info/WHEEL +5 -0
- unicex-0.13.17.dist-info/licenses/LICENSE +28 -0
- unicex-0.13.17.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2315 @@
|
|
|
1
|
+
__all__ = ["Client"]
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Literal, Self
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
import msgpack
|
|
8
|
+
from eth_account import Account
|
|
9
|
+
from eth_account.messages import encode_typed_data
|
|
10
|
+
from eth_account.signers.local import LocalAccount
|
|
11
|
+
from eth_utils.conversions import to_hex
|
|
12
|
+
from eth_utils.crypto import keccak
|
|
13
|
+
|
|
14
|
+
from unicex._base import BaseClient
|
|
15
|
+
from unicex.exceptions import NotAuthorized
|
|
16
|
+
from unicex.types import LoggerLike
|
|
17
|
+
from unicex.utils import filter_params
|
|
18
|
+
|
|
19
|
+
# Authentication
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _l1_payload(phantom_agent: dict[str, Any]) -> dict[str, Any]:
|
|
23
|
+
"""Формирует EIP-712 payload для подписи "агента".
|
|
24
|
+
|
|
25
|
+
Простыми словами:
|
|
26
|
+
Это упаковка данных в формат, который кошелёк сможет подписать.
|
|
27
|
+
В Ethereum есть стандарт EIP-712 — "structured data signing".
|
|
28
|
+
Он позволяет подписывать не просто строку, а структуру (объект),
|
|
29
|
+
чтобы потом её можно было проверить.
|
|
30
|
+
|
|
31
|
+
Пример:
|
|
32
|
+
>>> phantom = {"source": "a", "connectionId": b"1234...."}
|
|
33
|
+
>>> _l1_payload(phantom)
|
|
34
|
+
{...сложный словарь...}
|
|
35
|
+
|
|
36
|
+
Параметры:
|
|
37
|
+
phantom_agent (dict): объект с полями:
|
|
38
|
+
- source (str): откуда пришёл агент ("a" для mainnet, "b" для testnet)
|
|
39
|
+
- connectionId (bytes32): уникальный ID (обычно хэш)
|
|
40
|
+
|
|
41
|
+
Возвращает:
|
|
42
|
+
dict: структура для подписи через EIP-712
|
|
43
|
+
"""
|
|
44
|
+
return {
|
|
45
|
+
"domain": {
|
|
46
|
+
"chainId": 1337,
|
|
47
|
+
"name": "Exchange",
|
|
48
|
+
"verifyingContract": "0x0000000000000000000000000000000000000000",
|
|
49
|
+
"version": "1",
|
|
50
|
+
},
|
|
51
|
+
"types": {
|
|
52
|
+
"Agent": [
|
|
53
|
+
{"name": "source", "type": "string"},
|
|
54
|
+
{"name": "connectionId", "type": "bytes32"},
|
|
55
|
+
],
|
|
56
|
+
"EIP712Domain": [
|
|
57
|
+
{"name": "name", "type": "string"},
|
|
58
|
+
{"name": "version", "type": "string"},
|
|
59
|
+
{"name": "chainId", "type": "uint256"},
|
|
60
|
+
{"name": "verifyingContract", "type": "address"},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
"primaryType": "Agent",
|
|
64
|
+
"message": phantom_agent,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _address_to_bytes(address: str) -> bytes:
|
|
69
|
+
r"""Переводит Ethereum-адрес в байты.
|
|
70
|
+
|
|
71
|
+
Простыми словами:
|
|
72
|
+
Берём строку вида "0xABC123..." и превращаем её в бинарные данные.
|
|
73
|
+
Это нужно, потому что внутри подписи адрес должен храниться как массив байтов.
|
|
74
|
+
|
|
75
|
+
Пример:
|
|
76
|
+
>>> _address_to_bytes("0x0000000000000000000000000000000000000001")
|
|
77
|
+
b'\\x00...\\x01'
|
|
78
|
+
|
|
79
|
+
Параметры:
|
|
80
|
+
address (str): строковый Ethereum-адрес, с "0x" или без.
|
|
81
|
+
|
|
82
|
+
Возвращает:
|
|
83
|
+
bytes: бинарное представление адреса.
|
|
84
|
+
"""
|
|
85
|
+
return bytes.fromhex(address[2:] if address.startswith("0x") else address)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _construct_phantom_agent(hash: bytes, is_mainnet: bool) -> dict[str, Any]:
|
|
89
|
+
r"""Собирает объект "phantom_agent".
|
|
90
|
+
|
|
91
|
+
Простыми словами:
|
|
92
|
+
Это кусочек данных, который будет подписываться.
|
|
93
|
+
В нём указывается:
|
|
94
|
+
- источник ("a" если это mainnet, "b" если не mainnet)
|
|
95
|
+
- connectionId — хэш действий.
|
|
96
|
+
|
|
97
|
+
Пример:
|
|
98
|
+
>>> _construct_phantom_agent(b"\\x01" * 32, True)
|
|
99
|
+
{"source": "a", "connectionId": b"\\x01"*32}
|
|
100
|
+
|
|
101
|
+
Параметры:
|
|
102
|
+
hash (bytes): хэш действия (32 байта).
|
|
103
|
+
is_mainnet (bool): True если сеть основная, False если тестовая.
|
|
104
|
+
|
|
105
|
+
Возвращает:
|
|
106
|
+
dict: объект phantom_agent.
|
|
107
|
+
"""
|
|
108
|
+
return {"source": "a" if is_mainnet else "b", "connectionId": hash}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _action_hash(
|
|
112
|
+
action: dict[str, Any],
|
|
113
|
+
vault_address: str | None,
|
|
114
|
+
nonce: int,
|
|
115
|
+
expires_after: int | None,
|
|
116
|
+
) -> bytes:
|
|
117
|
+
r"""Строит хэш действия.
|
|
118
|
+
|
|
119
|
+
Простыми словами:
|
|
120
|
+
Берём действие (например, ордер), сериализуем его через msgpack,
|
|
121
|
+
добавляем nonce, адрес хранилища (если есть), срок действия,
|
|
122
|
+
и всё это хэшируем keccak256.
|
|
123
|
+
Получается уникальный "отпечаток" действия.
|
|
124
|
+
|
|
125
|
+
Пример:
|
|
126
|
+
>>> action = {"type": "order", "amount": 1}
|
|
127
|
+
>>> _action_hash(action, None, 42, None)
|
|
128
|
+
b"\\xab...\\xff" # 32 байта
|
|
129
|
+
|
|
130
|
+
Параметры:
|
|
131
|
+
action (dict): описание действия (например, ордер).
|
|
132
|
+
vault_address (str | None): адрес кошелька/контракта, если есть.
|
|
133
|
+
nonce (int): уникальный счётчик (чтобы нельзя было повторить действие).
|
|
134
|
+
expires_after (int | None): время (в секундах), когда действие протухает.
|
|
135
|
+
|
|
136
|
+
Возвращает:
|
|
137
|
+
bytes: 32-байтовый хэш (keccak256).
|
|
138
|
+
"""
|
|
139
|
+
data = msgpack.packb(action)
|
|
140
|
+
data += nonce.to_bytes(8, "big") # type: ignore
|
|
141
|
+
if vault_address is None:
|
|
142
|
+
data += b"\x00"
|
|
143
|
+
else:
|
|
144
|
+
data += b"\x01"
|
|
145
|
+
data += _address_to_bytes(vault_address)
|
|
146
|
+
if expires_after is not None:
|
|
147
|
+
data += b"\x00"
|
|
148
|
+
data += expires_after.to_bytes(8, "big")
|
|
149
|
+
return keccak(data)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _sign_inner(wallet: LocalAccount, data: dict[str, Any]) -> dict[str, Any]:
|
|
153
|
+
"""Подписывает данные EIP-712 через кошелёк.
|
|
154
|
+
|
|
155
|
+
Простыми словами:
|
|
156
|
+
Берём структуру (payload), кодируем её в формат EIP-712
|
|
157
|
+
и просим кошелёк подписать.
|
|
158
|
+
Возвращаем r, s, v — стандартные параметры Ethereum-подписи.
|
|
159
|
+
|
|
160
|
+
Пример:
|
|
161
|
+
>>> _sign_inner(wallet, {...})
|
|
162
|
+
{"r": "0x...", "s": "0x...", "v": 27}
|
|
163
|
+
|
|
164
|
+
Параметры:
|
|
165
|
+
wallet (LocalAccount): объект кошелька.
|
|
166
|
+
data (dict): структура для подписи (EIP-712).
|
|
167
|
+
|
|
168
|
+
Возвращает:
|
|
169
|
+
dict:
|
|
170
|
+
- r (str): часть подписи
|
|
171
|
+
- s (str): часть подписи
|
|
172
|
+
- v (int): "восстановитель" (27 или 28 обычно)
|
|
173
|
+
"""
|
|
174
|
+
structured_data = encode_typed_data(full_message=data)
|
|
175
|
+
signed = wallet.sign_message(structured_data)
|
|
176
|
+
return {"r": to_hex(signed["r"]), "s": to_hex(signed["s"]), "v": signed["v"]}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _sign_l1_action(
|
|
180
|
+
wallet: LocalAccount,
|
|
181
|
+
action: dict[str, Any],
|
|
182
|
+
active_pool: str | None,
|
|
183
|
+
nonce: int,
|
|
184
|
+
expires_after: int | None,
|
|
185
|
+
is_mainnet: bool = True,
|
|
186
|
+
) -> dict[str, Any]:
|
|
187
|
+
"""Подписывает действие для L1 (основного уровня).
|
|
188
|
+
|
|
189
|
+
Простыми словами:
|
|
190
|
+
Это конечная функция, которая собирает всё:
|
|
191
|
+
- делает хэш действия
|
|
192
|
+
- строит phantom_agent
|
|
193
|
+
- формирует payload для подписи
|
|
194
|
+
- подписывает его через кошелёк
|
|
195
|
+
Возвращает r, s, v.
|
|
196
|
+
|
|
197
|
+
Пример:
|
|
198
|
+
>>> _sign_l1_action(wallet, {"type": "order"}, None, 1, None, True)
|
|
199
|
+
{"r": "0x...", "s": "0x...", "v": 27}
|
|
200
|
+
|
|
201
|
+
Параметры:
|
|
202
|
+
wallet (LocalAccount): объект кошелька.
|
|
203
|
+
action (dict): действие (например, ордер).
|
|
204
|
+
active_pool (str | None): адрес пула (если нужен).
|
|
205
|
+
nonce (int): уникальный номер действия.
|
|
206
|
+
expires_after (int | None): срок жизни действия.
|
|
207
|
+
is_mainnet (bool): True — основная сеть, False — тестовая.
|
|
208
|
+
|
|
209
|
+
Возвращает:
|
|
210
|
+
dict:
|
|
211
|
+
- r (str)
|
|
212
|
+
- s (str)
|
|
213
|
+
- v (int)
|
|
214
|
+
"""
|
|
215
|
+
hash = _action_hash(action, active_pool, nonce, expires_after)
|
|
216
|
+
phantom_agent = _construct_phantom_agent(hash, is_mainnet)
|
|
217
|
+
data = _l1_payload(phantom_agent)
|
|
218
|
+
return _sign_inner(wallet, data)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _user_signed_payload(
|
|
222
|
+
primary_type: str,
|
|
223
|
+
payload_types: list[dict[str, str]],
|
|
224
|
+
action: dict[str, Any],
|
|
225
|
+
) -> dict[str, Any]:
|
|
226
|
+
"""Формирует EIP-712 payload для "user-signed" подписи.
|
|
227
|
+
|
|
228
|
+
Простыми словами:
|
|
229
|
+
Это структура для подписи действий от лица пользователя.
|
|
230
|
+
В отличие от L1-подписи (где используется phantom-агент),
|
|
231
|
+
здесь кошелёк подписывает само действие напрямую.
|
|
232
|
+
|
|
233
|
+
ChainID и домен указываются специально, чтобы предотвратить
|
|
234
|
+
повторное воспроизведение (replay) на других цепях.
|
|
235
|
+
|
|
236
|
+
Пример:
|
|
237
|
+
>>> payload = _user_signed_payload(
|
|
238
|
+
... primary_type="Withdraw",
|
|
239
|
+
... payload_types=[{"name": "amount", "type": "uint256"}],
|
|
240
|
+
... action={"amount": 100, "signatureChainId": "0x66eee"},
|
|
241
|
+
... )
|
|
242
|
+
>>> payload.keys()
|
|
243
|
+
dict_keys(["domain", "types", "primaryType", "message"])
|
|
244
|
+
|
|
245
|
+
Параметры:
|
|
246
|
+
primary_type (str): Основной тип сообщения, например `"Withdraw"`, `"Transfer"`.
|
|
247
|
+
payload_types (list[dict]): Список полей EIP-712 типа, например:
|
|
248
|
+
`[{"name": "amount", "type": "uint256"}, {"name": "recipient", "type": "address"}]`
|
|
249
|
+
action (dict): Само сообщение (поля, которые будут подписаны).
|
|
250
|
+
Должно содержать `"signatureChainId"` (строку в hex).
|
|
251
|
+
|
|
252
|
+
Возвращает:
|
|
253
|
+
dict: Полностью готовая структура EIP-712 для подписи кошельком.
|
|
254
|
+
"""
|
|
255
|
+
chain_id = int(action["signatureChainId"], 16)
|
|
256
|
+
return {
|
|
257
|
+
"domain": {
|
|
258
|
+
"name": "HyperliquidSignTransaction",
|
|
259
|
+
"version": "1",
|
|
260
|
+
"chainId": chain_id,
|
|
261
|
+
"verifyingContract": "0x0000000000000000000000000000000000000000",
|
|
262
|
+
},
|
|
263
|
+
"types": {
|
|
264
|
+
primary_type: payload_types,
|
|
265
|
+
"EIP712Domain": [
|
|
266
|
+
{"name": "name", "type": "string"},
|
|
267
|
+
{"name": "version", "type": "string"},
|
|
268
|
+
{"name": "chainId", "type": "uint256"},
|
|
269
|
+
{"name": "verifyingContract", "type": "address"},
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
"primaryType": primary_type,
|
|
273
|
+
"message": action,
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _sign_user_signed_action(
|
|
278
|
+
wallet: LocalAccount,
|
|
279
|
+
action: dict[str, Any],
|
|
280
|
+
payload_types: list[dict[str, str]],
|
|
281
|
+
primary_type: str,
|
|
282
|
+
is_mainnet: bool,
|
|
283
|
+
) -> dict[str, Any]:
|
|
284
|
+
"""Подписывает действие "user-signed" формата (EIP-712).
|
|
285
|
+
|
|
286
|
+
Простыми словами:
|
|
287
|
+
Это альтернатива L1-подписи. Здесь сам пользователь (его кошелёк)
|
|
288
|
+
подписывает объект `action`, который будет исполнен Hyperliquid.
|
|
289
|
+
Отличие от L1: используется другой `chainId` (0x66eee) и поле `"hyperliquidChain"`,
|
|
290
|
+
чтобы действие нельзя было воспроизвести в другой сети.
|
|
291
|
+
|
|
292
|
+
Пример:
|
|
293
|
+
>>> action = {"amount": 100}
|
|
294
|
+
>>> payload_types = [{"name": "amount", "type": "uint256"}]
|
|
295
|
+
>>> sig = _sign_user_signed_action(wallet, action, payload_types, "Withdraw", True)
|
|
296
|
+
>>> sig.keys()
|
|
297
|
+
dict_keys(["r", "s", "v"])
|
|
298
|
+
|
|
299
|
+
Параметры:
|
|
300
|
+
wallet (LocalAccount): Объект кошелька, созданный через `Account.from_key(...)`.
|
|
301
|
+
action (dict): Содержимое действия, которое нужно подписать.
|
|
302
|
+
payload_types (list[dict[str, str]]): Описание полей типа для EIP-712.
|
|
303
|
+
primary_type (str): Основное имя типа, например `"Withdraw"`, `"Transfer"`.
|
|
304
|
+
is_mainnet (bool): True — подпись для основной сети, False — для тестовой.
|
|
305
|
+
|
|
306
|
+
Возвращает:
|
|
307
|
+
dict:
|
|
308
|
+
- r (str): первая часть подписи
|
|
309
|
+
- s (str): вторая часть подписи
|
|
310
|
+
- v (int): "восстановитель" подписи (27 или 28)
|
|
311
|
+
"""
|
|
312
|
+
# signatureChainId — цепочка, через которую кошелёк делает подпись (не Hyperliquid chain)
|
|
313
|
+
action["signatureChainId"] = "0x66eee"
|
|
314
|
+
# hyperliquidChain — фактическая среда исполнения
|
|
315
|
+
action["hyperliquidChain"] = "Mainnet" if is_mainnet else "Testnet"
|
|
316
|
+
|
|
317
|
+
data = _user_signed_payload(primary_type, payload_types, action)
|
|
318
|
+
return _sign_inner(wallet, data)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
USD_SEND_SIGN_TYPES = [
|
|
322
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
323
|
+
{"name": "destination", "type": "string"},
|
|
324
|
+
{"name": "amount", "type": "string"},
|
|
325
|
+
{"name": "time", "type": "uint64"},
|
|
326
|
+
]
|
|
327
|
+
|
|
328
|
+
SPOT_TRANSFER_SIGN_TYPES = [
|
|
329
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
330
|
+
{"name": "destination", "type": "string"},
|
|
331
|
+
{"name": "token", "type": "string"},
|
|
332
|
+
{"name": "amount", "type": "string"},
|
|
333
|
+
{"name": "time", "type": "uint64"},
|
|
334
|
+
]
|
|
335
|
+
|
|
336
|
+
WITHDRAW_SIGN_TYPES = [
|
|
337
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
338
|
+
{"name": "destination", "type": "string"},
|
|
339
|
+
{"name": "amount", "type": "string"},
|
|
340
|
+
{"name": "time", "type": "uint64"},
|
|
341
|
+
]
|
|
342
|
+
|
|
343
|
+
USD_CLASS_TRANSFER_SIGN_TYPES = [
|
|
344
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
345
|
+
{"name": "amount", "type": "string"},
|
|
346
|
+
{"name": "toPerp", "type": "bool"},
|
|
347
|
+
{"name": "nonce", "type": "uint64"},
|
|
348
|
+
]
|
|
349
|
+
|
|
350
|
+
SEND_ASSET_SIGN_TYPES = [
|
|
351
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
352
|
+
{"name": "destination", "type": "string"},
|
|
353
|
+
{"name": "sourceDex", "type": "string"},
|
|
354
|
+
{"name": "destinationDex", "type": "string"},
|
|
355
|
+
{"name": "token", "type": "string"},
|
|
356
|
+
{"name": "amount", "type": "string"},
|
|
357
|
+
{"name": "fromSubAccount", "type": "string"},
|
|
358
|
+
{"name": "nonce", "type": "uint64"},
|
|
359
|
+
]
|
|
360
|
+
|
|
361
|
+
STAKING_SIGN_TYPES = [
|
|
362
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
363
|
+
{"name": "wei", "type": "uint64"},
|
|
364
|
+
{"name": "nonce", "type": "uint64"},
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
TOKEN_DELEGATE_TYPES = [
|
|
368
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
369
|
+
{"name": "validator", "type": "address"},
|
|
370
|
+
{"name": "wei", "type": "uint64"},
|
|
371
|
+
{"name": "isUndelegate", "type": "bool"},
|
|
372
|
+
{"name": "nonce", "type": "uint64"},
|
|
373
|
+
]
|
|
374
|
+
|
|
375
|
+
APPROVE_AGENT_SIGN_TYPES = [
|
|
376
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
377
|
+
{"name": "agentAddress", "type": "address"},
|
|
378
|
+
{"name": "agentName", "type": "string"},
|
|
379
|
+
{"name": "nonce", "type": "uint64"},
|
|
380
|
+
]
|
|
381
|
+
|
|
382
|
+
APPROVE_BUILDER_FEE_SIGN_TYPES = [
|
|
383
|
+
{"name": "hyperliquidChain", "type": "string"},
|
|
384
|
+
{"name": "maxFeeRate", "type": "string"},
|
|
385
|
+
{"name": "builder", "type": "address"},
|
|
386
|
+
{"name": "nonce", "type": "uint64"},
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
class Client(BaseClient):
|
|
391
|
+
"""Клиент для работы с Hyperliquid API."""
|
|
392
|
+
|
|
393
|
+
_BASE_URL = "https://api.hyperliquid.xyz"
|
|
394
|
+
"""Базовый URL для REST API Hyperliquid."""
|
|
395
|
+
|
|
396
|
+
_BASE_HEADERS = {"Content-Type": "application/json"}
|
|
397
|
+
|
|
398
|
+
def __init__(
|
|
399
|
+
self,
|
|
400
|
+
session: aiohttp.ClientSession,
|
|
401
|
+
private_key: str | bytes | None = None,
|
|
402
|
+
wallet_address: str | None = None,
|
|
403
|
+
vault_address: str | None = None,
|
|
404
|
+
logger: LoggerLike | None = None,
|
|
405
|
+
max_retries: int = 3,
|
|
406
|
+
retry_delay: int | float = 0.1,
|
|
407
|
+
proxies: list[str] | None = None,
|
|
408
|
+
timeout: int = 10,
|
|
409
|
+
) -> None:
|
|
410
|
+
"""Инициализация клиента.
|
|
411
|
+
|
|
412
|
+
Параметры:
|
|
413
|
+
session (`aiohttp.ClientSession`): Сессия для выполнения HTTP‑запросов.
|
|
414
|
+
private_key (`str | bytes | None`): Приватный ключ API для аутентификации (Hyperliquid).
|
|
415
|
+
wallet_address (`str | None`): Адрес кошелька для аутентификации (Hyperliquid).
|
|
416
|
+
vault_address (`str | None`): Адрес хранилища для аутентификации (Hyperliquid).
|
|
417
|
+
logger (`LoggerLike | None`): Логгер для вывода информации.
|
|
418
|
+
max_retries (`int`): Максимальное количество повторных попыток запроса.
|
|
419
|
+
retry_delay (`int | float`): Задержка между повторными попытками, сек.
|
|
420
|
+
proxies (`list[str] | None`): Список HTTP(S)‑прокси для циклического использования.
|
|
421
|
+
timeout (`int`): Максимальное время ожидания ответа от сервера, сек.
|
|
422
|
+
"""
|
|
423
|
+
super().__init__(
|
|
424
|
+
session,
|
|
425
|
+
None,
|
|
426
|
+
None,
|
|
427
|
+
None,
|
|
428
|
+
logger,
|
|
429
|
+
max_retries,
|
|
430
|
+
retry_delay,
|
|
431
|
+
proxies,
|
|
432
|
+
timeout,
|
|
433
|
+
)
|
|
434
|
+
self._vault_address = vault_address
|
|
435
|
+
self._wallet_address = wallet_address
|
|
436
|
+
self._wallet: LocalAccount | None = None
|
|
437
|
+
if private_key is not None:
|
|
438
|
+
# private_key может быть в hex-строке ("0x...") или в байтах
|
|
439
|
+
self._wallet = Account.from_key(private_key)
|
|
440
|
+
|
|
441
|
+
@classmethod
|
|
442
|
+
async def create(
|
|
443
|
+
cls,
|
|
444
|
+
private_key: str | bytes | None = None,
|
|
445
|
+
wallet_address: str | None = None,
|
|
446
|
+
vault_address: str | None = None,
|
|
447
|
+
session: aiohttp.ClientSession | None = None,
|
|
448
|
+
logger: LoggerLike | None = None,
|
|
449
|
+
max_retries: int = 3,
|
|
450
|
+
retry_delay: int | float = 0.1,
|
|
451
|
+
proxies: list[str] | None = None,
|
|
452
|
+
timeout: int = 10,
|
|
453
|
+
) -> Self:
|
|
454
|
+
"""Создаёт инстанцию клиента.
|
|
455
|
+
|
|
456
|
+
Параметры:
|
|
457
|
+
private_key (`str | bytes | None`): Приватный ключ для подписи запросов.
|
|
458
|
+
wallet_address (`str | None`): Адрес кошелька для подписи запросов.
|
|
459
|
+
vault_address (`str | None`): Адрес валита для подписи запросов.
|
|
460
|
+
session (`aiohttp.ClientSession | None`): Сессия для HTTP‑запросов (если не передана, будет создана).
|
|
461
|
+
logger (`LoggerLike | None`): Логгер для вывода информации.
|
|
462
|
+
max_retries (`int`): Максимум повторов при ошибках запроса.
|
|
463
|
+
retry_delay (`int | float`): Задержка между повторами, сек.
|
|
464
|
+
proxies (`list[str] | None`): Список HTTP(S)‑прокси.
|
|
465
|
+
timeout (`int`): Таймаут ответа сервера, сек.
|
|
466
|
+
|
|
467
|
+
Возвращает:
|
|
468
|
+
`Self`: Созданный экземпляр клиента.
|
|
469
|
+
"""
|
|
470
|
+
return cls(
|
|
471
|
+
private_key=private_key,
|
|
472
|
+
wallet_address=wallet_address,
|
|
473
|
+
vault_address=vault_address,
|
|
474
|
+
session=session or aiohttp.ClientSession(),
|
|
475
|
+
logger=logger,
|
|
476
|
+
max_retries=max_retries,
|
|
477
|
+
retry_delay=retry_delay,
|
|
478
|
+
proxies=proxies,
|
|
479
|
+
timeout=timeout,
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
async def _make_request(
|
|
483
|
+
self,
|
|
484
|
+
method: Literal["GET", "POST"],
|
|
485
|
+
endpoint: str,
|
|
486
|
+
*,
|
|
487
|
+
data: dict[str, Any] | None = None,
|
|
488
|
+
) -> Any:
|
|
489
|
+
"""Создаёт HTTP-запрос к Hyperliquid API.
|
|
490
|
+
|
|
491
|
+
Параметры:
|
|
492
|
+
method (`Literal["GET", "POST"]`): HTTP-метод запроса.
|
|
493
|
+
endpoint (`str`): Относительный путь эндпоинта.
|
|
494
|
+
data (`dict[str, Any] | None`): Тело запроса для POST-запросов.
|
|
495
|
+
|
|
496
|
+
Возвращает:
|
|
497
|
+
`Any`: Ответ Hyperliquid API.
|
|
498
|
+
"""
|
|
499
|
+
filtered_data = filter_params(data) if data is not None else None
|
|
500
|
+
|
|
501
|
+
return await super()._make_request(
|
|
502
|
+
method=method,
|
|
503
|
+
url=self._BASE_URL + endpoint,
|
|
504
|
+
headers=self._BASE_HEADERS,
|
|
505
|
+
data=filtered_data,
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
async def _post_request(self, endpoint: str, data: dict) -> Any:
|
|
509
|
+
"""Создание POST-запроса к Hyperliquid API."""
|
|
510
|
+
return await self._make_request("POST", endpoint, data=data)
|
|
511
|
+
|
|
512
|
+
async def request(
|
|
513
|
+
self,
|
|
514
|
+
endpoint: str,
|
|
515
|
+
*,
|
|
516
|
+
data: dict,
|
|
517
|
+
) -> Any:
|
|
518
|
+
"""Специальный метод для выполнения запросов на эндпоинты, которые не обернуты в клиенте.
|
|
519
|
+
|
|
520
|
+
Параметры:
|
|
521
|
+
endpoint (`str`): Относительный путь эндпоинта (например, `/info`).
|
|
522
|
+
data (`dict[str, Any]`): JSON-тело запроса для POST-запросов.
|
|
523
|
+
|
|
524
|
+
Возвращает:
|
|
525
|
+
`Any`: Ответ Hyperliquid API.
|
|
526
|
+
"""
|
|
527
|
+
return await self._post_request(endpoint, data=data)
|
|
528
|
+
|
|
529
|
+
# topic: Info endpoint
|
|
530
|
+
# topic: Futures
|
|
531
|
+
|
|
532
|
+
async def perp_dexs(self) -> list[dict | None]:
|
|
533
|
+
"""Получение списка доступных перпетуальных DEX.
|
|
534
|
+
|
|
535
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-metadata-universe-and-margin-tables
|
|
536
|
+
"""
|
|
537
|
+
payload = {"type": "perpDexs"}
|
|
538
|
+
|
|
539
|
+
return await self._post_request("/info", data=payload)
|
|
540
|
+
|
|
541
|
+
async def perp_metadata(self, dex: str | None = None) -> dict:
|
|
542
|
+
"""Получение метаданных по перпетуальным контрактам.
|
|
543
|
+
|
|
544
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-metadata-universe-and-margin-tables
|
|
545
|
+
"""
|
|
546
|
+
payload = {
|
|
547
|
+
"type": "meta",
|
|
548
|
+
"dex": dex,
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return await self._post_request("/info", data=payload)
|
|
552
|
+
|
|
553
|
+
async def perp_meta_and_asset_contexts(self) -> list[Any]:
|
|
554
|
+
"""Получение метаданных и контекстов активов перпетуальных контрактов.
|
|
555
|
+
|
|
556
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc
|
|
557
|
+
"""
|
|
558
|
+
payload = {"type": "metaAndAssetCtxs"}
|
|
559
|
+
|
|
560
|
+
return await self._post_request("/info", data=payload)
|
|
561
|
+
|
|
562
|
+
async def perp_account_summary(self, user: str, dex: str | None = None) -> dict:
|
|
563
|
+
"""Получение сводной информации по аккаунту пользователя в перпетуалах.
|
|
564
|
+
|
|
565
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary
|
|
566
|
+
"""
|
|
567
|
+
payload = {
|
|
568
|
+
"type": "clearinghouseState",
|
|
569
|
+
"user": user,
|
|
570
|
+
"dex": dex,
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return await self._post_request("/info", data=payload)
|
|
574
|
+
|
|
575
|
+
async def perp_user_funding_history(
|
|
576
|
+
self,
|
|
577
|
+
user: str,
|
|
578
|
+
start_time: int,
|
|
579
|
+
end_time: int | None = None,
|
|
580
|
+
) -> list[dict]:
|
|
581
|
+
"""Получение истории фондирования пользователя.
|
|
582
|
+
|
|
583
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-a-users-funding-history-or-non-funding-ledger-updates
|
|
584
|
+
"""
|
|
585
|
+
payload = {
|
|
586
|
+
"type": "userFunding",
|
|
587
|
+
"user": user,
|
|
588
|
+
"startTime": start_time,
|
|
589
|
+
"endTime": end_time,
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return await self._post_request("/info", data=payload)
|
|
593
|
+
|
|
594
|
+
async def perp_user_non_funding_ledger_updates(
|
|
595
|
+
self,
|
|
596
|
+
user: str,
|
|
597
|
+
start_time: int,
|
|
598
|
+
end_time: int | None = None,
|
|
599
|
+
) -> list[dict]:
|
|
600
|
+
"""Получение нефондировочных обновлений леджера пользователя.
|
|
601
|
+
|
|
602
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-a-users-funding-history-or-non-funding-ledger-updates
|
|
603
|
+
"""
|
|
604
|
+
payload = {
|
|
605
|
+
"type": "userNonFundingLedgerUpdates",
|
|
606
|
+
"user": user,
|
|
607
|
+
"startTime": start_time,
|
|
608
|
+
"endTime": end_time,
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return await self._post_request("/info", data=payload)
|
|
612
|
+
|
|
613
|
+
async def perp_funding_history(
|
|
614
|
+
self,
|
|
615
|
+
coin: str,
|
|
616
|
+
start_time: int,
|
|
617
|
+
end_time: int | None = None,
|
|
618
|
+
) -> list[dict]:
|
|
619
|
+
"""Получение исторических ставок фондирования по монете.
|
|
620
|
+
|
|
621
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-historical-funding-rates
|
|
622
|
+
"""
|
|
623
|
+
payload = {
|
|
624
|
+
"type": "fundingHistory",
|
|
625
|
+
"coin": coin,
|
|
626
|
+
"startTime": start_time,
|
|
627
|
+
"endTime": end_time,
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return await self._post_request("/info", data=payload)
|
|
631
|
+
|
|
632
|
+
async def perp_predicted_fundings(self) -> list[list[Any]]:
|
|
633
|
+
"""Получение предсказанных ставок фондирования по площадкам.
|
|
634
|
+
|
|
635
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-predicted-funding-rates-for-different-venues
|
|
636
|
+
"""
|
|
637
|
+
payload = {"type": "predictedFundings"}
|
|
638
|
+
|
|
639
|
+
return await self._post_request("/info", data=payload)
|
|
640
|
+
|
|
641
|
+
async def perps_at_open_interest_cap(self) -> list[str]:
|
|
642
|
+
"""Получение списка перпетуалов с достигнутым лимитом открытого интереса.
|
|
643
|
+
|
|
644
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#query-perps-at-open-interest-caps
|
|
645
|
+
"""
|
|
646
|
+
payload = {"type": "perpsAtOpenInterestCap"}
|
|
647
|
+
|
|
648
|
+
return await self._post_request("/info", data=payload)
|
|
649
|
+
|
|
650
|
+
async def perp_deploy_auction_status(self) -> dict:
|
|
651
|
+
"""Получение статуса аукциона развёртывания перпетуалов.
|
|
652
|
+
|
|
653
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-information-about-the-perp-deploy-auction
|
|
654
|
+
"""
|
|
655
|
+
payload = {"type": "perpDeployAuctionStatus"}
|
|
656
|
+
|
|
657
|
+
return await self._post_request("/info", data=payload)
|
|
658
|
+
|
|
659
|
+
async def perp_active_asset_data(self, user: str, coin: str) -> dict:
|
|
660
|
+
"""Получение активных параметров актива пользователя в перпетуалах.
|
|
661
|
+
|
|
662
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-active-asset-data
|
|
663
|
+
"""
|
|
664
|
+
payload = {
|
|
665
|
+
"type": "activeAssetData",
|
|
666
|
+
"user": user,
|
|
667
|
+
"coin": coin,
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return await self._post_request("/info", data=payload)
|
|
671
|
+
|
|
672
|
+
async def perp_dex_limits(self, dex: str) -> dict:
|
|
673
|
+
"""Получение лимитов для перпетуального DEX, развёрнутого билдерами.
|
|
674
|
+
|
|
675
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-builder-deployed-perp-market-limits
|
|
676
|
+
"""
|
|
677
|
+
payload = {
|
|
678
|
+
"type": "perpDexLimits",
|
|
679
|
+
"dex": dex,
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return await self._post_request("/info", data=payload)
|
|
683
|
+
|
|
684
|
+
async def open_orders(self, user: str, dex: str | None = None) -> list[dict]:
|
|
685
|
+
"""Получение списка открытых ордеров пользователя.
|
|
686
|
+
|
|
687
|
+
https://docs.chainstack.com/reference/hyperliquid-info-openorders
|
|
688
|
+
"""
|
|
689
|
+
payload = {
|
|
690
|
+
"type": "openOrders",
|
|
691
|
+
"user": user,
|
|
692
|
+
"dex": dex,
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return await self._post_request("/info", data=payload)
|
|
696
|
+
|
|
697
|
+
async def exchange_status(self) -> dict:
|
|
698
|
+
"""Получение текущего статуса биржи Hyperliquid.
|
|
699
|
+
|
|
700
|
+
https://docs.chainstack.com/reference/hyperliquid-info-exchangestatus
|
|
701
|
+
"""
|
|
702
|
+
payload = {"type": "exchangeStatus"}
|
|
703
|
+
|
|
704
|
+
return await self._post_request("/info", data=payload)
|
|
705
|
+
|
|
706
|
+
async def frontend_open_orders(self, user: str, dex: str | None = None) -> list[dict]:
|
|
707
|
+
"""Получение открытых ордеров в формате фронтенда.
|
|
708
|
+
|
|
709
|
+
https://docs.chainstack.com/reference/hyperliquid-info-frontendopenorders
|
|
710
|
+
"""
|
|
711
|
+
payload = {
|
|
712
|
+
"type": "frontendOpenOrders",
|
|
713
|
+
"user": user,
|
|
714
|
+
"dex": dex,
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return await self._post_request("/info", data=payload)
|
|
718
|
+
|
|
719
|
+
async def liquidatable(self, user: str) -> dict:
|
|
720
|
+
"""Проверка, подлежит ли аккаунт пользователя ликвидации.
|
|
721
|
+
|
|
722
|
+
https://docs.chainstack.com/reference/hyperliquid-info-liquidatable
|
|
723
|
+
"""
|
|
724
|
+
payload = {
|
|
725
|
+
"type": "liquidatable",
|
|
726
|
+
"user": user,
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return await self._post_request("/info", data=payload)
|
|
730
|
+
|
|
731
|
+
async def max_market_order_ntls(self) -> list[dict]:
|
|
732
|
+
"""Получение максимальных объёмов рыночных ордеров по активам.
|
|
733
|
+
|
|
734
|
+
https://docs.chainstack.com/reference/hyperliquid-info-maxmarketorderntls
|
|
735
|
+
"""
|
|
736
|
+
payload = {"type": "maxMarketOrderNtls"}
|
|
737
|
+
|
|
738
|
+
return await self._post_request("/info", data=payload)
|
|
739
|
+
|
|
740
|
+
async def vault_summaries(self) -> list[dict]:
|
|
741
|
+
"""Получение сводки по всем доступным вултам (vaults).
|
|
742
|
+
|
|
743
|
+
https://docs.chainstack.com/reference/hyperliquid-info-vaultsummaries
|
|
744
|
+
"""
|
|
745
|
+
payload = {"type": "vaultSummaries"}
|
|
746
|
+
|
|
747
|
+
return await self._post_request("/info", data=payload)
|
|
748
|
+
|
|
749
|
+
async def user_vault_equities(self, user: str) -> list[dict]:
|
|
750
|
+
"""Получение данных об инвестициях пользователя в вулты (vaults).
|
|
751
|
+
|
|
752
|
+
https://docs.chainstack.com/reference/hyperliquid-info-uservaultequities
|
|
753
|
+
"""
|
|
754
|
+
payload = {
|
|
755
|
+
"type": "userVaultEquities",
|
|
756
|
+
"user": user,
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
return await self._post_request("/info", data=payload)
|
|
760
|
+
|
|
761
|
+
async def leading_vaults(self, user: str) -> list[dict]:
|
|
762
|
+
"""Получение списка вултов (vaults), которыми управляет пользователь.
|
|
763
|
+
|
|
764
|
+
https://docs.chainstack.com/reference/hyperliquid-info-leadingvaults
|
|
765
|
+
"""
|
|
766
|
+
payload = {
|
|
767
|
+
"type": "leadingVaults",
|
|
768
|
+
"user": user,
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
return await self._post_request("/info", data=payload)
|
|
772
|
+
|
|
773
|
+
async def extra_agents(self, user: str) -> list[dict]:
|
|
774
|
+
"""Получение списка дополнительных агентов пользователя.
|
|
775
|
+
|
|
776
|
+
https://docs.chainstack.com/reference/hyperliquid-info-extraagents
|
|
777
|
+
"""
|
|
778
|
+
payload = {
|
|
779
|
+
"type": "extraAgents",
|
|
780
|
+
"user": user,
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
return await self._post_request("/info", data=payload)
|
|
784
|
+
|
|
785
|
+
async def sub_accounts(self, user: str) -> list[dict]:
|
|
786
|
+
"""Получение списка саб-аккаунтов пользователя.
|
|
787
|
+
|
|
788
|
+
https://docs.chainstack.com/reference/hyperliquid-info-subaccounts
|
|
789
|
+
"""
|
|
790
|
+
payload = {
|
|
791
|
+
"type": "subAccounts",
|
|
792
|
+
"user": user,
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return await self._post_request("/info", data=payload)
|
|
796
|
+
|
|
797
|
+
async def user_fees(self, user: str) -> dict:
|
|
798
|
+
"""Получение информации о торговых комиссиях пользователя.
|
|
799
|
+
|
|
800
|
+
https://docs.chainstack.com/reference/hyperliquid-info-userfees
|
|
801
|
+
"""
|
|
802
|
+
payload = {
|
|
803
|
+
"type": "userFees",
|
|
804
|
+
"user": user,
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return await self._post_request("/info", data=payload)
|
|
808
|
+
|
|
809
|
+
async def user_rate_limit(self, user: str) -> dict:
|
|
810
|
+
"""Получение сведений о лимитах запросов пользователя.
|
|
811
|
+
|
|
812
|
+
https://docs.chainstack.com/reference/hyperliquid-info-userratelimit
|
|
813
|
+
"""
|
|
814
|
+
payload = {
|
|
815
|
+
"type": "userRateLimit",
|
|
816
|
+
"user": user,
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
return await self._post_request("/info", data=payload)
|
|
820
|
+
|
|
821
|
+
async def delegations(self, user: str) -> list[dict]:
|
|
822
|
+
"""Получение списка делегаций пользователя.
|
|
823
|
+
|
|
824
|
+
https://docs.chainstack.com/reference/hyperliquid-info-delegations
|
|
825
|
+
"""
|
|
826
|
+
payload = {
|
|
827
|
+
"type": "delegations",
|
|
828
|
+
"user": user,
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return await self._post_request("/info", data=payload)
|
|
832
|
+
|
|
833
|
+
async def delegator_summary(self, user: str) -> dict:
|
|
834
|
+
"""Получение сводки по делегациям пользователя.
|
|
835
|
+
|
|
836
|
+
https://docs.chainstack.com/reference/hyperliquid-info-delegator-summary
|
|
837
|
+
"""
|
|
838
|
+
payload = {
|
|
839
|
+
"type": "delegatorSummary",
|
|
840
|
+
"user": user,
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return await self._post_request("/info", data=payload)
|
|
844
|
+
|
|
845
|
+
async def max_builder_fee(self, user: str, builder: str) -> int:
|
|
846
|
+
"""Получение максимальной комиссии билдера, одобренной пользователем.
|
|
847
|
+
|
|
848
|
+
https://docs.chainstack.com/reference/hyperliquid-info-max-builder-fee
|
|
849
|
+
"""
|
|
850
|
+
payload = {
|
|
851
|
+
"type": "maxBuilderFee",
|
|
852
|
+
"user": user,
|
|
853
|
+
"builder": builder,
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
return await self._post_request("/info", data=payload)
|
|
857
|
+
|
|
858
|
+
async def user_to_multi_sig_signers(self, user: str) -> list[str]:
|
|
859
|
+
"""Получение списка подписантов мультисиг-кошелька пользователя.
|
|
860
|
+
|
|
861
|
+
https://docs.chainstack.com/reference/hyperliquid-info-user-to-multi-sig-signers
|
|
862
|
+
"""
|
|
863
|
+
payload = {
|
|
864
|
+
"type": "userToMultiSigSigners",
|
|
865
|
+
"user": user,
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return await self._post_request("/info", data=payload)
|
|
869
|
+
|
|
870
|
+
async def user_role(self, user: str) -> dict:
|
|
871
|
+
"""Получение информации о роли пользователя в системе.
|
|
872
|
+
|
|
873
|
+
https://docs.chainstack.com/reference/hyperliquid-info-user-role
|
|
874
|
+
"""
|
|
875
|
+
payload = {
|
|
876
|
+
"type": "userRole",
|
|
877
|
+
"user": user,
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
return await self._post_request("/info", data=payload)
|
|
881
|
+
|
|
882
|
+
async def validator_l1_votes(self) -> list[dict]:
|
|
883
|
+
"""Получение сведений о голосах валидаторов на L1.
|
|
884
|
+
|
|
885
|
+
https://docs.chainstack.com/reference/hyperliquid-info-validator-l1-votes
|
|
886
|
+
"""
|
|
887
|
+
payload = {"type": "validatorL1Votes"}
|
|
888
|
+
|
|
889
|
+
return await self._post_request("/info", data=payload)
|
|
890
|
+
|
|
891
|
+
async def web_data2(self) -> dict:
|
|
892
|
+
"""Получение агрегированных данных для веб-интерфейса.
|
|
893
|
+
|
|
894
|
+
https://docs.chainstack.com/reference/hyperliquid-info-web-data2
|
|
895
|
+
"""
|
|
896
|
+
payload = {"type": "webData2"}
|
|
897
|
+
|
|
898
|
+
return await self._post_request("/info", data=payload)
|
|
899
|
+
|
|
900
|
+
async def all_mids(self, dex: str | None = None) -> dict:
|
|
901
|
+
"""Получение текущих средних цен по всем активам.
|
|
902
|
+
|
|
903
|
+
https://docs.chainstack.com/reference/hyperliquid-info-allmids
|
|
904
|
+
"""
|
|
905
|
+
payload = {
|
|
906
|
+
"type": "allMids",
|
|
907
|
+
"dex": dex,
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
return await self._post_request("/info", data=payload)
|
|
911
|
+
|
|
912
|
+
async def user_fills(
|
|
913
|
+
self,
|
|
914
|
+
user: str,
|
|
915
|
+
aggregate_by_time: bool | None = None,
|
|
916
|
+
) -> list[dict]:
|
|
917
|
+
"""Получение последних исполнений ордеров пользователя.
|
|
918
|
+
|
|
919
|
+
https://docs.chainstack.com/reference/hyperliquid-info-user-fills
|
|
920
|
+
"""
|
|
921
|
+
payload = {
|
|
922
|
+
"type": "userFills",
|
|
923
|
+
"user": user,
|
|
924
|
+
"aggregateByTime": aggregate_by_time,
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
return await self._post_request("/info", data=payload)
|
|
928
|
+
|
|
929
|
+
async def user_fills_by_time(
|
|
930
|
+
self,
|
|
931
|
+
user: str,
|
|
932
|
+
start_time: int,
|
|
933
|
+
end_time: int | None = None,
|
|
934
|
+
aggregate_by_time: bool | None = None,
|
|
935
|
+
) -> list[dict]:
|
|
936
|
+
"""Получение исполнений ордеров пользователя за период.
|
|
937
|
+
|
|
938
|
+
https://docs.chainstack.com/reference/hyperliquid-info-user-fills-by-time
|
|
939
|
+
"""
|
|
940
|
+
payload = {
|
|
941
|
+
"type": "userFillsByTime",
|
|
942
|
+
"user": user,
|
|
943
|
+
"startTime": start_time,
|
|
944
|
+
"endTime": end_time,
|
|
945
|
+
"aggregateByTime": aggregate_by_time,
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return await self._post_request("/info", data=payload)
|
|
949
|
+
|
|
950
|
+
async def order_status(self, user: str, oid: int | str) -> dict:
|
|
951
|
+
"""Получение статуса ордера по идентификатору.
|
|
952
|
+
|
|
953
|
+
https://docs.chainstack.com/reference/hyperliquid-info-order-status
|
|
954
|
+
"""
|
|
955
|
+
payload = {
|
|
956
|
+
"type": "orderStatus",
|
|
957
|
+
"user": user,
|
|
958
|
+
"oid": oid,
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return await self._post_request("/info", data=payload)
|
|
962
|
+
|
|
963
|
+
async def l2_book(
|
|
964
|
+
self,
|
|
965
|
+
coin: str,
|
|
966
|
+
n_sig_figs: Literal[2, 3, 4, 5] | None = None,
|
|
967
|
+
mantissa: Literal[1, 2, 5] | None = None,
|
|
968
|
+
) -> list[list[dict]]:
|
|
969
|
+
"""Получение снапшота стакана уровня L2 для актива.
|
|
970
|
+
|
|
971
|
+
https://docs.chainstack.com/reference/hyperliquid-info-l2-book
|
|
972
|
+
"""
|
|
973
|
+
payload = {
|
|
974
|
+
"type": "l2Book",
|
|
975
|
+
"coin": coin,
|
|
976
|
+
"nSigFigs": n_sig_figs,
|
|
977
|
+
"mantissa": mantissa,
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
return await self._post_request("/info", data=payload)
|
|
981
|
+
|
|
982
|
+
async def batch_clearinghouse_states(
|
|
983
|
+
self,
|
|
984
|
+
users: list[str],
|
|
985
|
+
dex: str | None = None,
|
|
986
|
+
) -> list[dict | None]:
|
|
987
|
+
"""Получение сводок фьючерсных аккаунтов группы пользователей.
|
|
988
|
+
|
|
989
|
+
https://docs.chainstack.com/reference/hyperliquid-info-batch-clearinghouse-states
|
|
990
|
+
"""
|
|
991
|
+
payload = {
|
|
992
|
+
"type": "batchClearinghouseStates",
|
|
993
|
+
"users": users,
|
|
994
|
+
"dex": dex,
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
return await self._post_request("/info", data=payload)
|
|
998
|
+
|
|
999
|
+
async def candle_snapshot(
|
|
1000
|
+
self,
|
|
1001
|
+
coin: str,
|
|
1002
|
+
interval: str,
|
|
1003
|
+
start_time: int,
|
|
1004
|
+
end_time: int,
|
|
1005
|
+
) -> list[dict]:
|
|
1006
|
+
"""Получение датасета свечей за указанный период.
|
|
1007
|
+
|
|
1008
|
+
https://docs.chainstack.com/reference/hyperliquid-info-candle-snapshot
|
|
1009
|
+
"""
|
|
1010
|
+
payload = {
|
|
1011
|
+
"type": "candleSnapshot",
|
|
1012
|
+
"req": {
|
|
1013
|
+
"coin": coin,
|
|
1014
|
+
"interval": interval,
|
|
1015
|
+
"startTime": start_time,
|
|
1016
|
+
"endTime": end_time,
|
|
1017
|
+
},
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
return await self._post_request("/info", data=payload)
|
|
1021
|
+
|
|
1022
|
+
async def historical_orders(self, user: str) -> list[dict]:
|
|
1023
|
+
"""Получение истории ордеров пользователя.
|
|
1024
|
+
|
|
1025
|
+
https://docs.chainstack.com/reference/hyperliquid-info-historical-orders
|
|
1026
|
+
"""
|
|
1027
|
+
payload = {
|
|
1028
|
+
"type": "historicalOrders",
|
|
1029
|
+
"user": user,
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
return await self._post_request("/info", data=payload)
|
|
1033
|
+
|
|
1034
|
+
async def user_twap_slice_fills(self, user: str) -> list[dict]:
|
|
1035
|
+
"""Получение последних TWAP-исполнений пользователя.
|
|
1036
|
+
|
|
1037
|
+
https://docs.chainstack.com/reference/hyperliquid-info-user-twap-slice-fills
|
|
1038
|
+
"""
|
|
1039
|
+
payload = {
|
|
1040
|
+
"type": "userTwapSliceFills",
|
|
1041
|
+
"user": user,
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return await self._post_request("/info", data=payload)
|
|
1045
|
+
|
|
1046
|
+
async def recent_trades(self, coin: str) -> list[dict]:
|
|
1047
|
+
"""Получение последних публичных сделок по активу.
|
|
1048
|
+
|
|
1049
|
+
https://docs.chainstack.com/reference/hyperliquid-info-recent-trades
|
|
1050
|
+
"""
|
|
1051
|
+
payload = {
|
|
1052
|
+
"type": "recentTrades",
|
|
1053
|
+
"coin": coin,
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
return await self._post_request("/info", data=payload)
|
|
1057
|
+
|
|
1058
|
+
async def vault_details(self, vault_address: str, user: str | None = None) -> dict:
|
|
1059
|
+
"""Получение подробной информации о выбранном вулте.
|
|
1060
|
+
|
|
1061
|
+
https://docs.chainstack.com/reference/hyperliquid-info-vault-details
|
|
1062
|
+
"""
|
|
1063
|
+
payload = {
|
|
1064
|
+
"type": "vaultDetails",
|
|
1065
|
+
"vaultAddress": vault_address,
|
|
1066
|
+
"user": user,
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
return await self._post_request("/info", data=payload)
|
|
1070
|
+
|
|
1071
|
+
async def portfolio(self, user: str) -> list[list[Any]]:
|
|
1072
|
+
"""Получение данных о производительности портфеля пользователя.
|
|
1073
|
+
|
|
1074
|
+
https://docs.chainstack.com/reference/hyperliquid-info-portfolio
|
|
1075
|
+
"""
|
|
1076
|
+
payload = {
|
|
1077
|
+
"type": "portfolio",
|
|
1078
|
+
"user": user,
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
return await self._post_request("/info", data=payload)
|
|
1082
|
+
|
|
1083
|
+
async def referral(self, user: str) -> dict:
|
|
1084
|
+
"""Получение реферальной информации пользователя.
|
|
1085
|
+
|
|
1086
|
+
https://docs.chainstack.com/reference/hyperliquid-info-referral
|
|
1087
|
+
"""
|
|
1088
|
+
payload = {
|
|
1089
|
+
"type": "referral",
|
|
1090
|
+
"user": user,
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
return await self._post_request("/info", data=payload)
|
|
1094
|
+
|
|
1095
|
+
async def delegator_rewards(self, user: str) -> list[dict]:
|
|
1096
|
+
"""Получение истории стейкинг-наград пользователя.
|
|
1097
|
+
|
|
1098
|
+
https://docs.chainstack.com/reference/hyperliquid-info-delegator-rewards
|
|
1099
|
+
"""
|
|
1100
|
+
payload = {
|
|
1101
|
+
"type": "delegatorRewards",
|
|
1102
|
+
"user": user,
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
return await self._post_request("/info", data=payload)
|
|
1106
|
+
|
|
1107
|
+
async def gossip_root_ips(self) -> list[str]:
|
|
1108
|
+
"""Получение списка узлов для P2P-госсипа.
|
|
1109
|
+
|
|
1110
|
+
https://docs.chainstack.com/reference/hyperliquid-info-gossip-root-ips
|
|
1111
|
+
"""
|
|
1112
|
+
payload = {"type": "gossipRootIps"}
|
|
1113
|
+
|
|
1114
|
+
return await self._post_request("/info", data=payload)
|
|
1115
|
+
|
|
1116
|
+
# topic: Spot
|
|
1117
|
+
|
|
1118
|
+
async def spot_metadata(self) -> dict:
|
|
1119
|
+
"""Получение спотовой метаинформации о бирже.
|
|
1120
|
+
|
|
1121
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-metadata
|
|
1122
|
+
"""
|
|
1123
|
+
payload = {"type": "spotMeta"}
|
|
1124
|
+
|
|
1125
|
+
return await self._post_request("/info", data=payload)
|
|
1126
|
+
|
|
1127
|
+
async def spot_meta_and_asset_contexts(self) -> list[Any]:
|
|
1128
|
+
"""Получение метаданных и контекстов спотовых активов.
|
|
1129
|
+
|
|
1130
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts
|
|
1131
|
+
"""
|
|
1132
|
+
payload = {"type": "spotMetaAndAssetCtxs"}
|
|
1133
|
+
|
|
1134
|
+
return await self._post_request("/info", data=payload)
|
|
1135
|
+
|
|
1136
|
+
async def spot_token_balances(self, user: str) -> dict:
|
|
1137
|
+
"""Получение балансов токенов пользователя на спотовом рынке.
|
|
1138
|
+
|
|
1139
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-a-users-token-balances
|
|
1140
|
+
"""
|
|
1141
|
+
payload = {
|
|
1142
|
+
"type": "spotClearinghouseState",
|
|
1143
|
+
"user": user,
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
return await self._post_request("/info", data=payload)
|
|
1147
|
+
|
|
1148
|
+
async def spot_deploy_state(self, user: str) -> dict:
|
|
1149
|
+
"""Получение информации об аукционе развёртывания спотового токена.
|
|
1150
|
+
|
|
1151
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-information-about-the-spot-deploy-auction
|
|
1152
|
+
"""
|
|
1153
|
+
payload = {
|
|
1154
|
+
"type": "spotDeployState",
|
|
1155
|
+
"user": user,
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return await self._post_request("/info", data=payload)
|
|
1159
|
+
|
|
1160
|
+
async def spot_pair_deploy_auction_status(self) -> dict:
|
|
1161
|
+
"""Получение статуса аукциона развёртывания спотовых пар.
|
|
1162
|
+
|
|
1163
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-information-about-the-spot-pair-deploy-auction
|
|
1164
|
+
"""
|
|
1165
|
+
payload = {"type": "spotPairDeployAuctionStatus"}
|
|
1166
|
+
|
|
1167
|
+
return await self._post_request("/info", data=payload)
|
|
1168
|
+
|
|
1169
|
+
async def spot_token_details(self, token_id: str) -> dict:
|
|
1170
|
+
"""Получение подробной информации о спотовом токене.
|
|
1171
|
+
|
|
1172
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-information-about-a-token
|
|
1173
|
+
"""
|
|
1174
|
+
payload = {
|
|
1175
|
+
"type": "tokenDetails",
|
|
1176
|
+
"tokenId": token_id,
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
return await self._post_request("/info", data=payload)
|
|
1180
|
+
|
|
1181
|
+
# topic: Exchange endpoint
|
|
1182
|
+
|
|
1183
|
+
async def place_order(
|
|
1184
|
+
self,
|
|
1185
|
+
asset: int,
|
|
1186
|
+
is_buy: bool,
|
|
1187
|
+
size: str,
|
|
1188
|
+
reduce_only: bool,
|
|
1189
|
+
order_type: Literal["limit", "trigger"],
|
|
1190
|
+
order_body: dict,
|
|
1191
|
+
price: str | None = None,
|
|
1192
|
+
client_order_id: str | None = None,
|
|
1193
|
+
grouping: Literal["na", "normalTpsl", "positionTpsl"] = "na",
|
|
1194
|
+
builder_address: str | None = None,
|
|
1195
|
+
builder_fee: int | None = None,
|
|
1196
|
+
nonce: int | None = None,
|
|
1197
|
+
expires_after: int | None = None,
|
|
1198
|
+
vault_address: str | None = None,
|
|
1199
|
+
) -> dict:
|
|
1200
|
+
"""Выставление ордера на бирже.
|
|
1201
|
+
|
|
1202
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
|
|
1203
|
+
"""
|
|
1204
|
+
order_payload = {
|
|
1205
|
+
"a": asset,
|
|
1206
|
+
"b": is_buy,
|
|
1207
|
+
"p": price,
|
|
1208
|
+
"s": size,
|
|
1209
|
+
"r": reduce_only,
|
|
1210
|
+
"t": {order_type: order_body},
|
|
1211
|
+
}
|
|
1212
|
+
if client_order_id is not None:
|
|
1213
|
+
order_payload["c"] = client_order_id
|
|
1214
|
+
|
|
1215
|
+
return await self.batch_place_orders(
|
|
1216
|
+
[order_payload],
|
|
1217
|
+
grouping=grouping,
|
|
1218
|
+
builder_address=builder_address,
|
|
1219
|
+
builder_fee=builder_fee,
|
|
1220
|
+
nonce=nonce,
|
|
1221
|
+
expires_after=expires_after,
|
|
1222
|
+
vault_address=vault_address,
|
|
1223
|
+
)
|
|
1224
|
+
|
|
1225
|
+
async def batch_place_orders(
|
|
1226
|
+
self,
|
|
1227
|
+
orders: list[dict[str, Any]],
|
|
1228
|
+
grouping: Literal["na", "normalTpsl", "positionTpsl"] = "na",
|
|
1229
|
+
builder_address: str | None = None,
|
|
1230
|
+
builder_fee: int | None = None,
|
|
1231
|
+
nonce: int | None = None,
|
|
1232
|
+
expires_after: int | None = None,
|
|
1233
|
+
vault_address: str | None = None,
|
|
1234
|
+
) -> dict:
|
|
1235
|
+
"""Пакетное выставление ордеров на бирже.
|
|
1236
|
+
|
|
1237
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order
|
|
1238
|
+
"""
|
|
1239
|
+
if not orders:
|
|
1240
|
+
raise ValueError("orders must not be empty")
|
|
1241
|
+
if self._wallet is None:
|
|
1242
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1243
|
+
if builder_address is not None and builder_fee is None:
|
|
1244
|
+
raise TypeError("builder_fee is required when builder_address is provided")
|
|
1245
|
+
if builder_address is None and builder_fee is not None:
|
|
1246
|
+
raise TypeError("builder_address is required when builder_fee is provided")
|
|
1247
|
+
|
|
1248
|
+
required_keys = {"a", "b", "p", "s", "r", "t"}
|
|
1249
|
+
normalized_orders = []
|
|
1250
|
+
for order in orders:
|
|
1251
|
+
missing_keys = required_keys - order.keys()
|
|
1252
|
+
if missing_keys:
|
|
1253
|
+
missing = ", ".join(sorted(missing_keys))
|
|
1254
|
+
raise ValueError(f"order is missing required fields: {missing}")
|
|
1255
|
+
normalized = dict(order)
|
|
1256
|
+
normalized["p"] = str(normalized["p"])
|
|
1257
|
+
normalized["s"] = str(normalized["s"])
|
|
1258
|
+
if normalized.get("c") is None:
|
|
1259
|
+
normalized.pop("c", None)
|
|
1260
|
+
normalized_orders.append(normalized)
|
|
1261
|
+
|
|
1262
|
+
action = {
|
|
1263
|
+
"type": "order",
|
|
1264
|
+
"orders": normalized_orders,
|
|
1265
|
+
"grouping": grouping,
|
|
1266
|
+
}
|
|
1267
|
+
if builder_address is not None:
|
|
1268
|
+
action["builder"] = {"b": builder_address, "f": builder_fee}
|
|
1269
|
+
|
|
1270
|
+
effective_vault = vault_address or self._vault_address
|
|
1271
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
1272
|
+
signature = _sign_l1_action(
|
|
1273
|
+
self._wallet,
|
|
1274
|
+
action,
|
|
1275
|
+
effective_vault,
|
|
1276
|
+
action_nonce,
|
|
1277
|
+
expires_after,
|
|
1278
|
+
)
|
|
1279
|
+
|
|
1280
|
+
payload = {
|
|
1281
|
+
"action": action,
|
|
1282
|
+
"nonce": action_nonce,
|
|
1283
|
+
"signature": signature,
|
|
1284
|
+
}
|
|
1285
|
+
if effective_vault is not None:
|
|
1286
|
+
payload["vaultAddress"] = effective_vault
|
|
1287
|
+
if expires_after is not None:
|
|
1288
|
+
payload["expiresAfter"] = expires_after
|
|
1289
|
+
|
|
1290
|
+
return await self._post_request("/exchange", data=payload)
|
|
1291
|
+
|
|
1292
|
+
async def cancel_order(
|
|
1293
|
+
self,
|
|
1294
|
+
asset: int,
|
|
1295
|
+
order_id: int,
|
|
1296
|
+
nonce: int | None = None,
|
|
1297
|
+
expires_after: int | None = None,
|
|
1298
|
+
vault_address: str | None = None,
|
|
1299
|
+
) -> dict:
|
|
1300
|
+
"""Отмена ордера по идентификатору.
|
|
1301
|
+
|
|
1302
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
|
|
1303
|
+
"""
|
|
1304
|
+
return await self.cancel_orders(
|
|
1305
|
+
cancels=[{"a": asset, "o": order_id}],
|
|
1306
|
+
nonce=nonce,
|
|
1307
|
+
expires_after=expires_after,
|
|
1308
|
+
vault_address=vault_address,
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1311
|
+
async def cancel_orders(
|
|
1312
|
+
self,
|
|
1313
|
+
cancels: list[dict[str, int | str]],
|
|
1314
|
+
nonce: int | None = None,
|
|
1315
|
+
expires_after: int | None = None,
|
|
1316
|
+
vault_address: str | None = None,
|
|
1317
|
+
) -> dict:
|
|
1318
|
+
"""Отмена ордеров по идентификатору ордера.
|
|
1319
|
+
|
|
1320
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s
|
|
1321
|
+
"""
|
|
1322
|
+
if not cancels:
|
|
1323
|
+
raise ValueError("cancels must not be empty")
|
|
1324
|
+
if self._wallet is None:
|
|
1325
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1326
|
+
|
|
1327
|
+
normalized: list[dict[str, int]] = []
|
|
1328
|
+
for cancel in cancels:
|
|
1329
|
+
missing_keys = {"a", "o"} - cancel.keys()
|
|
1330
|
+
if missing_keys:
|
|
1331
|
+
missing = ", ".join(sorted(missing_keys))
|
|
1332
|
+
raise ValueError(f"cancel entry is missing required fields: {missing}")
|
|
1333
|
+
normalized.append(
|
|
1334
|
+
{
|
|
1335
|
+
"a": int(cancel["a"]),
|
|
1336
|
+
"o": int(cancel["o"]),
|
|
1337
|
+
}
|
|
1338
|
+
)
|
|
1339
|
+
|
|
1340
|
+
action = {"type": "cancel", "cancels": normalized}
|
|
1341
|
+
|
|
1342
|
+
effective_vault = vault_address or self._vault_address
|
|
1343
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
1344
|
+
signature = _sign_l1_action(
|
|
1345
|
+
self._wallet,
|
|
1346
|
+
action,
|
|
1347
|
+
effective_vault,
|
|
1348
|
+
action_nonce,
|
|
1349
|
+
expires_after,
|
|
1350
|
+
)
|
|
1351
|
+
|
|
1352
|
+
payload: dict[str, Any] = {
|
|
1353
|
+
"action": action,
|
|
1354
|
+
"nonce": action_nonce,
|
|
1355
|
+
"signature": signature,
|
|
1356
|
+
}
|
|
1357
|
+
if effective_vault is not None:
|
|
1358
|
+
payload["vaultAddress"] = effective_vault
|
|
1359
|
+
if expires_after is not None:
|
|
1360
|
+
payload["expiresAfter"] = expires_after
|
|
1361
|
+
|
|
1362
|
+
return await self._post_request("/exchange", data=payload)
|
|
1363
|
+
|
|
1364
|
+
async def cancel_order_by_cloid(
|
|
1365
|
+
self,
|
|
1366
|
+
asset: int,
|
|
1367
|
+
client_order_id: str,
|
|
1368
|
+
nonce: int | None = None,
|
|
1369
|
+
expires_after: int | None = None,
|
|
1370
|
+
vault_address: str | None = None,
|
|
1371
|
+
) -> dict:
|
|
1372
|
+
"""Отмена ордера по клиентскому идентификатору.
|
|
1373
|
+
|
|
1374
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
|
|
1375
|
+
"""
|
|
1376
|
+
return await self.cancel_orders_by_cloid(
|
|
1377
|
+
cancels=[{"asset": asset, "cloid": client_order_id}],
|
|
1378
|
+
nonce=nonce,
|
|
1379
|
+
expires_after=expires_after,
|
|
1380
|
+
vault_address=vault_address,
|
|
1381
|
+
)
|
|
1382
|
+
|
|
1383
|
+
async def cancel_orders_by_cloid(
|
|
1384
|
+
self,
|
|
1385
|
+
cancels: list[dict[str, Any]],
|
|
1386
|
+
nonce: int | None = None,
|
|
1387
|
+
expires_after: int | None = None,
|
|
1388
|
+
vault_address: str | None = None,
|
|
1389
|
+
) -> dict:
|
|
1390
|
+
"""Отмена ордеров по клиентскому идентификатору.
|
|
1391
|
+
|
|
1392
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
|
|
1393
|
+
"""
|
|
1394
|
+
if not cancels:
|
|
1395
|
+
raise ValueError("cancels must not be empty")
|
|
1396
|
+
if self._wallet is None:
|
|
1397
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1398
|
+
|
|
1399
|
+
normalized: list[dict[str, Any]] = []
|
|
1400
|
+
for cancel in cancels:
|
|
1401
|
+
missing_keys = {"asset", "cloid"} - cancel.keys()
|
|
1402
|
+
if missing_keys:
|
|
1403
|
+
missing = ", ".join(sorted(missing_keys))
|
|
1404
|
+
raise ValueError(f"cancel entry is missing required fields: {missing}")
|
|
1405
|
+
normalized.append(
|
|
1406
|
+
{
|
|
1407
|
+
"asset": int(cancel["asset"]),
|
|
1408
|
+
"cloid": str(cancel["cloid"]),
|
|
1409
|
+
}
|
|
1410
|
+
)
|
|
1411
|
+
|
|
1412
|
+
action = {"type": "cancelByCloid", "cancels": normalized}
|
|
1413
|
+
|
|
1414
|
+
effective_vault = vault_address or self._vault_address
|
|
1415
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
1416
|
+
signature = _sign_l1_action(
|
|
1417
|
+
self._wallet,
|
|
1418
|
+
action,
|
|
1419
|
+
effective_vault,
|
|
1420
|
+
action_nonce,
|
|
1421
|
+
expires_after,
|
|
1422
|
+
)
|
|
1423
|
+
|
|
1424
|
+
payload: dict[str, Any] = {
|
|
1425
|
+
"action": action,
|
|
1426
|
+
"nonce": action_nonce,
|
|
1427
|
+
"signature": signature,
|
|
1428
|
+
}
|
|
1429
|
+
if effective_vault is not None:
|
|
1430
|
+
payload["vaultAddress"] = effective_vault
|
|
1431
|
+
if expires_after is not None:
|
|
1432
|
+
payload["expiresAfter"] = expires_after
|
|
1433
|
+
|
|
1434
|
+
return await self._post_request("/exchange", data=payload)
|
|
1435
|
+
|
|
1436
|
+
async def schedule_cancel(
|
|
1437
|
+
self,
|
|
1438
|
+
time_ms: int | None = None,
|
|
1439
|
+
nonce: int | None = None,
|
|
1440
|
+
expires_after: int | None = None,
|
|
1441
|
+
vault_address: str | None = None,
|
|
1442
|
+
) -> dict:
|
|
1443
|
+
"""Планирование массовой отмены ордеров.
|
|
1444
|
+
|
|
1445
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#schedule-cancel-dead-mans-switch
|
|
1446
|
+
"""
|
|
1447
|
+
if self._wallet is None:
|
|
1448
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1449
|
+
|
|
1450
|
+
action: dict[str, Any] = {"type": "scheduleCancel"}
|
|
1451
|
+
if time_ms is not None:
|
|
1452
|
+
action["time"] = time_ms
|
|
1453
|
+
|
|
1454
|
+
effective_vault = vault_address or self._vault_address
|
|
1455
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
1456
|
+
signature = _sign_l1_action(
|
|
1457
|
+
self._wallet,
|
|
1458
|
+
action,
|
|
1459
|
+
effective_vault,
|
|
1460
|
+
action_nonce,
|
|
1461
|
+
expires_after,
|
|
1462
|
+
)
|
|
1463
|
+
|
|
1464
|
+
payload: dict[str, Any] = {
|
|
1465
|
+
"action": action,
|
|
1466
|
+
"nonce": action_nonce,
|
|
1467
|
+
"signature": signature,
|
|
1468
|
+
}
|
|
1469
|
+
if effective_vault is not None:
|
|
1470
|
+
payload["vaultAddress"] = effective_vault
|
|
1471
|
+
if expires_after is not None:
|
|
1472
|
+
payload["expiresAfter"] = expires_after
|
|
1473
|
+
|
|
1474
|
+
return await self._post_request("/exchange", data=payload)
|
|
1475
|
+
|
|
1476
|
+
async def modify_order(
|
|
1477
|
+
self,
|
|
1478
|
+
order_id: int | str,
|
|
1479
|
+
asset: int,
|
|
1480
|
+
is_buy: bool,
|
|
1481
|
+
price: str | float,
|
|
1482
|
+
size: str | float,
|
|
1483
|
+
reduce_only: bool,
|
|
1484
|
+
order_type: Literal["limit", "trigger"],
|
|
1485
|
+
order_body: dict[str, Any],
|
|
1486
|
+
client_order_id: str | None = None,
|
|
1487
|
+
nonce: int | None = None,
|
|
1488
|
+
expires_after: int | None = None,
|
|
1489
|
+
vault_address: str | None = None,
|
|
1490
|
+
) -> dict:
|
|
1491
|
+
"""Модификация существующего ордера.
|
|
1492
|
+
|
|
1493
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-an-order
|
|
1494
|
+
"""
|
|
1495
|
+
order_payload: dict[str, Any] = {
|
|
1496
|
+
"a": asset,
|
|
1497
|
+
"b": is_buy,
|
|
1498
|
+
"p": str(price),
|
|
1499
|
+
"s": str(size),
|
|
1500
|
+
"r": reduce_only,
|
|
1501
|
+
"t": {order_type: order_body},
|
|
1502
|
+
}
|
|
1503
|
+
if client_order_id is not None:
|
|
1504
|
+
order_payload["c"] = client_order_id
|
|
1505
|
+
|
|
1506
|
+
return await self.batch_modify_orders(
|
|
1507
|
+
modifies=[{"oid": order_id, "order": order_payload}],
|
|
1508
|
+
nonce=nonce,
|
|
1509
|
+
expires_after=expires_after,
|
|
1510
|
+
vault_address=vault_address,
|
|
1511
|
+
)
|
|
1512
|
+
|
|
1513
|
+
async def batch_modify_orders(
|
|
1514
|
+
self,
|
|
1515
|
+
modifies: list[dict[str, Any]],
|
|
1516
|
+
nonce: int | None = None,
|
|
1517
|
+
expires_after: int | None = None,
|
|
1518
|
+
vault_address: str | None = None,
|
|
1519
|
+
) -> dict:
|
|
1520
|
+
"""Пакетная модификация ордеров.
|
|
1521
|
+
|
|
1522
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders
|
|
1523
|
+
"""
|
|
1524
|
+
if not modifies:
|
|
1525
|
+
raise ValueError("modifies must not be empty")
|
|
1526
|
+
if self._wallet is None:
|
|
1527
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1528
|
+
|
|
1529
|
+
normalized: list[dict[str, Any]] = []
|
|
1530
|
+
for modify in modifies:
|
|
1531
|
+
missing_keys = {"oid", "order"} - modify.keys()
|
|
1532
|
+
if missing_keys:
|
|
1533
|
+
missing = ", ".join(sorted(missing_keys))
|
|
1534
|
+
raise ValueError(f"modify entry is missing required fields: {missing}")
|
|
1535
|
+
order = dict(modify["order"])
|
|
1536
|
+
required_order_keys = {"a", "b", "p", "s", "r", "t"}
|
|
1537
|
+
missing_order_keys = required_order_keys - order.keys()
|
|
1538
|
+
if missing_order_keys:
|
|
1539
|
+
missing = ", ".join(sorted(missing_order_keys))
|
|
1540
|
+
raise ValueError(f"order payload is missing required fields: {missing}")
|
|
1541
|
+
order["p"] = str(order["p"])
|
|
1542
|
+
order["s"] = str(order["s"])
|
|
1543
|
+
if order.get("c") is None:
|
|
1544
|
+
order.pop("c", None)
|
|
1545
|
+
normalized.append(
|
|
1546
|
+
{
|
|
1547
|
+
"oid": modify["oid"],
|
|
1548
|
+
"order": order,
|
|
1549
|
+
}
|
|
1550
|
+
)
|
|
1551
|
+
|
|
1552
|
+
action = {"type": "batchModify", "modifies": normalized}
|
|
1553
|
+
|
|
1554
|
+
effective_vault = vault_address or self._vault_address
|
|
1555
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
1556
|
+
signature = _sign_l1_action(
|
|
1557
|
+
self._wallet,
|
|
1558
|
+
action,
|
|
1559
|
+
effective_vault,
|
|
1560
|
+
action_nonce,
|
|
1561
|
+
expires_after,
|
|
1562
|
+
)
|
|
1563
|
+
|
|
1564
|
+
payload: dict[str, Any] = {
|
|
1565
|
+
"action": action,
|
|
1566
|
+
"nonce": action_nonce,
|
|
1567
|
+
"signature": signature,
|
|
1568
|
+
}
|
|
1569
|
+
if effective_vault is not None:
|
|
1570
|
+
payload["vaultAddress"] = effective_vault
|
|
1571
|
+
if expires_after is not None:
|
|
1572
|
+
payload["expiresAfter"] = expires_after
|
|
1573
|
+
|
|
1574
|
+
return await self._post_request("/exchange", data=payload)
|
|
1575
|
+
|
|
1576
|
+
async def update_leverage(
|
|
1577
|
+
self,
|
|
1578
|
+
asset: int,
|
|
1579
|
+
is_cross: bool,
|
|
1580
|
+
leverage: int,
|
|
1581
|
+
nonce: int | None = None,
|
|
1582
|
+
expires_after: int | None = None,
|
|
1583
|
+
vault_address: str | None = None,
|
|
1584
|
+
) -> dict:
|
|
1585
|
+
"""Обновление кредитного плеча по активу.
|
|
1586
|
+
|
|
1587
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-leverage
|
|
1588
|
+
"""
|
|
1589
|
+
if self._wallet is None:
|
|
1590
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1591
|
+
|
|
1592
|
+
action = {
|
|
1593
|
+
"type": "updateLeverage",
|
|
1594
|
+
"asset": asset,
|
|
1595
|
+
"isCross": is_cross,
|
|
1596
|
+
"leverage": leverage,
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
effective_vault = vault_address or self._vault_address
|
|
1600
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
1601
|
+
signature = _sign_l1_action(
|
|
1602
|
+
self._wallet,
|
|
1603
|
+
action,
|
|
1604
|
+
effective_vault,
|
|
1605
|
+
action_nonce,
|
|
1606
|
+
expires_after,
|
|
1607
|
+
)
|
|
1608
|
+
|
|
1609
|
+
payload: dict[str, Any] = {
|
|
1610
|
+
"action": action,
|
|
1611
|
+
"nonce": action_nonce,
|
|
1612
|
+
"signature": signature,
|
|
1613
|
+
}
|
|
1614
|
+
if effective_vault is not None:
|
|
1615
|
+
payload["vaultAddress"] = effective_vault
|
|
1616
|
+
if expires_after is not None:
|
|
1617
|
+
payload["expiresAfter"] = expires_after
|
|
1618
|
+
|
|
1619
|
+
return await self._post_request("/exchange", data=payload)
|
|
1620
|
+
|
|
1621
|
+
async def update_isolated_margin(
|
|
1622
|
+
self,
|
|
1623
|
+
asset: int,
|
|
1624
|
+
is_buy: bool,
|
|
1625
|
+
notional_change: int,
|
|
1626
|
+
nonce: int | None = None,
|
|
1627
|
+
expires_after: int | None = None,
|
|
1628
|
+
vault_address: str | None = None,
|
|
1629
|
+
) -> dict:
|
|
1630
|
+
"""Обновление маржи изолированной позиции.
|
|
1631
|
+
|
|
1632
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#update-isolated-margin
|
|
1633
|
+
"""
|
|
1634
|
+
if self._wallet is None:
|
|
1635
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1636
|
+
|
|
1637
|
+
action = {
|
|
1638
|
+
"type": "updateIsolatedMargin",
|
|
1639
|
+
"asset": asset,
|
|
1640
|
+
"isBuy": is_buy,
|
|
1641
|
+
"ntli": notional_change,
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
effective_vault = vault_address or self._vault_address
|
|
1645
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
1646
|
+
signature = _sign_l1_action(
|
|
1647
|
+
self._wallet,
|
|
1648
|
+
action,
|
|
1649
|
+
effective_vault,
|
|
1650
|
+
action_nonce,
|
|
1651
|
+
expires_after,
|
|
1652
|
+
)
|
|
1653
|
+
|
|
1654
|
+
payload: dict[str, Any] = {
|
|
1655
|
+
"action": action,
|
|
1656
|
+
"nonce": action_nonce,
|
|
1657
|
+
"signature": signature,
|
|
1658
|
+
}
|
|
1659
|
+
if effective_vault is not None:
|
|
1660
|
+
payload["vaultAddress"] = effective_vault
|
|
1661
|
+
if expires_after is not None:
|
|
1662
|
+
payload["expiresAfter"] = expires_after
|
|
1663
|
+
|
|
1664
|
+
return await self._post_request("/exchange", data=payload)
|
|
1665
|
+
|
|
1666
|
+
async def usd_send(
|
|
1667
|
+
self,
|
|
1668
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1669
|
+
signature_chain_id: str,
|
|
1670
|
+
destination: str,
|
|
1671
|
+
amount: str,
|
|
1672
|
+
time_ms: int,
|
|
1673
|
+
nonce: int | None = None,
|
|
1674
|
+
) -> dict:
|
|
1675
|
+
"""Перевод USDC между пользователями Hyperliquid.
|
|
1676
|
+
|
|
1677
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#core-usdc-transfer
|
|
1678
|
+
"""
|
|
1679
|
+
if self._wallet is None:
|
|
1680
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1681
|
+
|
|
1682
|
+
action = {
|
|
1683
|
+
"type": "usdSend",
|
|
1684
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
1685
|
+
"signatureChainId": signature_chain_id,
|
|
1686
|
+
"destination": destination,
|
|
1687
|
+
"amount": amount,
|
|
1688
|
+
"time": time_ms,
|
|
1689
|
+
}
|
|
1690
|
+
action_nonce = nonce if nonce is not None else time_ms
|
|
1691
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
1692
|
+
signature = _sign_user_signed_action(
|
|
1693
|
+
self._wallet,
|
|
1694
|
+
action,
|
|
1695
|
+
USD_SEND_SIGN_TYPES,
|
|
1696
|
+
"HyperliquidTransaction:UsdSend",
|
|
1697
|
+
is_mainnet,
|
|
1698
|
+
)
|
|
1699
|
+
|
|
1700
|
+
payload = {
|
|
1701
|
+
"action": action,
|
|
1702
|
+
"nonce": action_nonce,
|
|
1703
|
+
"signature": signature,
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
return await self._post_request("/exchange", data=payload)
|
|
1707
|
+
|
|
1708
|
+
async def spot_send(
|
|
1709
|
+
self,
|
|
1710
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1711
|
+
signature_chain_id: str,
|
|
1712
|
+
destination: str,
|
|
1713
|
+
token: str,
|
|
1714
|
+
amount: str,
|
|
1715
|
+
time_ms: int,
|
|
1716
|
+
nonce: int | None = None,
|
|
1717
|
+
) -> dict:
|
|
1718
|
+
"""Перевод спотового актива между пользователями Hyperliquid.
|
|
1719
|
+
|
|
1720
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#core-spot-transfer
|
|
1721
|
+
"""
|
|
1722
|
+
if self._wallet is None:
|
|
1723
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1724
|
+
|
|
1725
|
+
action = {
|
|
1726
|
+
"type": "spotSend",
|
|
1727
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
1728
|
+
"signatureChainId": signature_chain_id,
|
|
1729
|
+
"destination": destination,
|
|
1730
|
+
"token": token,
|
|
1731
|
+
"amount": amount,
|
|
1732
|
+
"time": time_ms,
|
|
1733
|
+
}
|
|
1734
|
+
action_nonce = nonce if nonce is not None else time_ms
|
|
1735
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
1736
|
+
signature = _sign_user_signed_action(
|
|
1737
|
+
self._wallet,
|
|
1738
|
+
action,
|
|
1739
|
+
SPOT_TRANSFER_SIGN_TYPES,
|
|
1740
|
+
"HyperliquidTransaction:SpotSend",
|
|
1741
|
+
is_mainnet,
|
|
1742
|
+
)
|
|
1743
|
+
|
|
1744
|
+
payload = {
|
|
1745
|
+
"action": action,
|
|
1746
|
+
"nonce": action_nonce,
|
|
1747
|
+
"signature": signature,
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
return await self._post_request("/exchange", data=payload)
|
|
1751
|
+
|
|
1752
|
+
async def initiate_withdrawal(
|
|
1753
|
+
self,
|
|
1754
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1755
|
+
signature_chain_id: str,
|
|
1756
|
+
amount: str,
|
|
1757
|
+
time_ms: int,
|
|
1758
|
+
destination: str,
|
|
1759
|
+
nonce: int | None = None,
|
|
1760
|
+
) -> dict:
|
|
1761
|
+
"""Инициация вывода средств на L1.
|
|
1762
|
+
|
|
1763
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#initiate-a-withdrawal-request
|
|
1764
|
+
"""
|
|
1765
|
+
if self._wallet is None:
|
|
1766
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1767
|
+
|
|
1768
|
+
action = {
|
|
1769
|
+
"type": "withdraw3",
|
|
1770
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
1771
|
+
"signatureChainId": signature_chain_id,
|
|
1772
|
+
"amount": amount,
|
|
1773
|
+
"time": time_ms,
|
|
1774
|
+
"destination": destination,
|
|
1775
|
+
}
|
|
1776
|
+
action_nonce = nonce if nonce is not None else time_ms
|
|
1777
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
1778
|
+
signature = _sign_user_signed_action(
|
|
1779
|
+
self._wallet,
|
|
1780
|
+
action,
|
|
1781
|
+
WITHDRAW_SIGN_TYPES,
|
|
1782
|
+
"HyperliquidTransaction:Withdraw",
|
|
1783
|
+
is_mainnet,
|
|
1784
|
+
)
|
|
1785
|
+
|
|
1786
|
+
payload = {
|
|
1787
|
+
"action": action,
|
|
1788
|
+
"nonce": action_nonce,
|
|
1789
|
+
"signature": signature,
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
return await self._post_request("/exchange", data=payload)
|
|
1793
|
+
|
|
1794
|
+
async def usd_class_transfer(
|
|
1795
|
+
self,
|
|
1796
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1797
|
+
signature_chain_id: str,
|
|
1798
|
+
amount: str,
|
|
1799
|
+
to_perp: bool,
|
|
1800
|
+
subaccount: str | None = None,
|
|
1801
|
+
) -> dict:
|
|
1802
|
+
"""Перевод USDC между спотовым и перпетуальным аккаунтами.
|
|
1803
|
+
|
|
1804
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#transfer-from-spot-account-to-perp-account-and-vice-versa
|
|
1805
|
+
"""
|
|
1806
|
+
if self._wallet is None:
|
|
1807
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1808
|
+
|
|
1809
|
+
amount_field = amount
|
|
1810
|
+
if subaccount is not None:
|
|
1811
|
+
amount_field = f"{amount} subaccount:{subaccount}"
|
|
1812
|
+
|
|
1813
|
+
nonce = int(time.time() * 1000)
|
|
1814
|
+
|
|
1815
|
+
action = {
|
|
1816
|
+
"type": "usdClassTransfer",
|
|
1817
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
1818
|
+
"signatureChainId": signature_chain_id,
|
|
1819
|
+
"amount": amount_field,
|
|
1820
|
+
"toPerp": to_perp,
|
|
1821
|
+
"nonce": nonce,
|
|
1822
|
+
}
|
|
1823
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
1824
|
+
signature = _sign_user_signed_action(
|
|
1825
|
+
self._wallet,
|
|
1826
|
+
action,
|
|
1827
|
+
USD_CLASS_TRANSFER_SIGN_TYPES,
|
|
1828
|
+
"HyperliquidTransaction:UsdClassTransfer",
|
|
1829
|
+
is_mainnet,
|
|
1830
|
+
)
|
|
1831
|
+
|
|
1832
|
+
payload = {
|
|
1833
|
+
"action": action,
|
|
1834
|
+
"nonce": nonce,
|
|
1835
|
+
"signature": signature,
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
return await self._post_request("/exchange", data=payload)
|
|
1839
|
+
|
|
1840
|
+
async def send_asset(
|
|
1841
|
+
self,
|
|
1842
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1843
|
+
signature_chain_id: str,
|
|
1844
|
+
destination: str,
|
|
1845
|
+
source_dex: str,
|
|
1846
|
+
destination_dex: str,
|
|
1847
|
+
token: str,
|
|
1848
|
+
amount: str,
|
|
1849
|
+
from_subaccount: str,
|
|
1850
|
+
nonce_value: int,
|
|
1851
|
+
nonce: int | None = None,
|
|
1852
|
+
) -> dict:
|
|
1853
|
+
"""Перевод токена между балансами и субаккаунтами (тестнет).
|
|
1854
|
+
|
|
1855
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#send-asset-testnet-only
|
|
1856
|
+
"""
|
|
1857
|
+
if self._wallet is None:
|
|
1858
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1859
|
+
|
|
1860
|
+
action = {
|
|
1861
|
+
"type": "sendAsset",
|
|
1862
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
1863
|
+
"signatureChainId": signature_chain_id,
|
|
1864
|
+
"destination": destination,
|
|
1865
|
+
"sourceDex": source_dex,
|
|
1866
|
+
"destinationDex": destination_dex,
|
|
1867
|
+
"token": token,
|
|
1868
|
+
"amount": amount,
|
|
1869
|
+
"fromSubAccount": from_subaccount,
|
|
1870
|
+
"nonce": nonce_value,
|
|
1871
|
+
}
|
|
1872
|
+
action_nonce = nonce if nonce is not None else nonce_value
|
|
1873
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
1874
|
+
signature = _sign_user_signed_action(
|
|
1875
|
+
self._wallet,
|
|
1876
|
+
action,
|
|
1877
|
+
SEND_ASSET_SIGN_TYPES,
|
|
1878
|
+
"HyperliquidTransaction:SendAsset",
|
|
1879
|
+
is_mainnet,
|
|
1880
|
+
)
|
|
1881
|
+
|
|
1882
|
+
payload = {
|
|
1883
|
+
"action": action,
|
|
1884
|
+
"nonce": action_nonce,
|
|
1885
|
+
"signature": signature,
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
return await self._post_request("/exchange", data=payload)
|
|
1889
|
+
|
|
1890
|
+
async def staking_deposit(
|
|
1891
|
+
self,
|
|
1892
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1893
|
+
signature_chain_id: str,
|
|
1894
|
+
wei_amount: int,
|
|
1895
|
+
nonce_value: int,
|
|
1896
|
+
nonce: int | None = None,
|
|
1897
|
+
) -> dict:
|
|
1898
|
+
"""Депозит нативного токена в стейкинг.
|
|
1899
|
+
|
|
1900
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#deposit-into-staking
|
|
1901
|
+
"""
|
|
1902
|
+
if self._wallet is None:
|
|
1903
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1904
|
+
|
|
1905
|
+
action = {
|
|
1906
|
+
"type": "cDeposit",
|
|
1907
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
1908
|
+
"signatureChainId": signature_chain_id,
|
|
1909
|
+
"wei": wei_amount,
|
|
1910
|
+
"nonce": nonce_value,
|
|
1911
|
+
}
|
|
1912
|
+
action_nonce = nonce if nonce is not None else nonce_value
|
|
1913
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
1914
|
+
signature = _sign_user_signed_action(
|
|
1915
|
+
self._wallet,
|
|
1916
|
+
action,
|
|
1917
|
+
STAKING_SIGN_TYPES,
|
|
1918
|
+
"HyperliquidTransaction:CDeposit",
|
|
1919
|
+
is_mainnet,
|
|
1920
|
+
)
|
|
1921
|
+
|
|
1922
|
+
payload = {
|
|
1923
|
+
"action": action,
|
|
1924
|
+
"nonce": action_nonce,
|
|
1925
|
+
"signature": signature,
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
return await self._post_request("/exchange", data=payload)
|
|
1929
|
+
|
|
1930
|
+
async def staking_withdraw(
|
|
1931
|
+
self,
|
|
1932
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1933
|
+
signature_chain_id: str,
|
|
1934
|
+
wei_amount: int,
|
|
1935
|
+
nonce_value: int,
|
|
1936
|
+
nonce: int | None = None,
|
|
1937
|
+
) -> dict:
|
|
1938
|
+
"""Вывод нативного токена из стейкинга.
|
|
1939
|
+
|
|
1940
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#withdraw-from-staking
|
|
1941
|
+
"""
|
|
1942
|
+
if self._wallet is None:
|
|
1943
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1944
|
+
|
|
1945
|
+
action = {
|
|
1946
|
+
"type": "cWithdraw",
|
|
1947
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
1948
|
+
"signatureChainId": signature_chain_id,
|
|
1949
|
+
"wei": wei_amount,
|
|
1950
|
+
"nonce": nonce_value,
|
|
1951
|
+
}
|
|
1952
|
+
action_nonce = nonce if nonce is not None else nonce_value
|
|
1953
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
1954
|
+
signature = _sign_user_signed_action(
|
|
1955
|
+
self._wallet,
|
|
1956
|
+
action,
|
|
1957
|
+
STAKING_SIGN_TYPES,
|
|
1958
|
+
"HyperliquidTransaction:CWithdraw",
|
|
1959
|
+
is_mainnet,
|
|
1960
|
+
)
|
|
1961
|
+
|
|
1962
|
+
payload = {
|
|
1963
|
+
"action": action,
|
|
1964
|
+
"nonce": action_nonce,
|
|
1965
|
+
"signature": signature,
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
return await self._post_request("/exchange", data=payload)
|
|
1969
|
+
|
|
1970
|
+
async def token_delegate(
|
|
1971
|
+
self,
|
|
1972
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1973
|
+
signature_chain_id: str,
|
|
1974
|
+
validator: str,
|
|
1975
|
+
is_undelegate: bool,
|
|
1976
|
+
wei_amount: int,
|
|
1977
|
+
nonce_value: int,
|
|
1978
|
+
nonce: int | None = None,
|
|
1979
|
+
) -> dict:
|
|
1980
|
+
"""Делегирование или отзыв делегирования нативного токена валидатору.
|
|
1981
|
+
|
|
1982
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#delegate-or-undelegate-stake-from-validator
|
|
1983
|
+
"""
|
|
1984
|
+
if self._wallet is None:
|
|
1985
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
1986
|
+
|
|
1987
|
+
action = {
|
|
1988
|
+
"type": "tokenDelegate",
|
|
1989
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
1990
|
+
"signatureChainId": signature_chain_id,
|
|
1991
|
+
"validator": validator,
|
|
1992
|
+
"isUndelegate": is_undelegate,
|
|
1993
|
+
"wei": wei_amount,
|
|
1994
|
+
"nonce": nonce_value,
|
|
1995
|
+
}
|
|
1996
|
+
action_nonce = nonce if nonce is not None else nonce_value
|
|
1997
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
1998
|
+
signature = _sign_user_signed_action(
|
|
1999
|
+
self._wallet,
|
|
2000
|
+
action,
|
|
2001
|
+
TOKEN_DELEGATE_TYPES,
|
|
2002
|
+
"HyperliquidTransaction:TokenDelegate",
|
|
2003
|
+
is_mainnet,
|
|
2004
|
+
)
|
|
2005
|
+
|
|
2006
|
+
payload = {
|
|
2007
|
+
"action": action,
|
|
2008
|
+
"nonce": action_nonce,
|
|
2009
|
+
"signature": signature,
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
return await self._post_request("/exchange", data=payload)
|
|
2013
|
+
|
|
2014
|
+
async def vault_transfer(
|
|
2015
|
+
self,
|
|
2016
|
+
vault_address: str,
|
|
2017
|
+
is_deposit: bool,
|
|
2018
|
+
usd: int,
|
|
2019
|
+
nonce: int | None = None,
|
|
2020
|
+
expires_after: int | None = None,
|
|
2021
|
+
signing_vault_address: str | None = None,
|
|
2022
|
+
) -> dict:
|
|
2023
|
+
"""Перевод средств в или из хранилища.
|
|
2024
|
+
|
|
2025
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#deposit-or-withdraw-from-a-vault
|
|
2026
|
+
"""
|
|
2027
|
+
if self._wallet is None:
|
|
2028
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
2029
|
+
|
|
2030
|
+
action = {
|
|
2031
|
+
"type": "vaultTransfer",
|
|
2032
|
+
"vaultAddress": vault_address,
|
|
2033
|
+
"isDeposit": is_deposit,
|
|
2034
|
+
"usd": usd,
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
effective_vault = signing_vault_address or self._vault_address
|
|
2038
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
2039
|
+
signature = _sign_l1_action(
|
|
2040
|
+
self._wallet,
|
|
2041
|
+
action,
|
|
2042
|
+
effective_vault,
|
|
2043
|
+
action_nonce,
|
|
2044
|
+
expires_after,
|
|
2045
|
+
)
|
|
2046
|
+
|
|
2047
|
+
payload: dict[str, Any] = {
|
|
2048
|
+
"action": action,
|
|
2049
|
+
"nonce": action_nonce,
|
|
2050
|
+
"signature": signature,
|
|
2051
|
+
}
|
|
2052
|
+
if effective_vault is not None:
|
|
2053
|
+
payload["vaultAddress"] = effective_vault
|
|
2054
|
+
if expires_after is not None:
|
|
2055
|
+
payload["expiresAfter"] = expires_after
|
|
2056
|
+
|
|
2057
|
+
return await self._post_request("/exchange", data=payload)
|
|
2058
|
+
|
|
2059
|
+
async def approve_agent(
|
|
2060
|
+
self,
|
|
2061
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
2062
|
+
signature_chain_id: str,
|
|
2063
|
+
agent_address: str,
|
|
2064
|
+
nonce_value: int,
|
|
2065
|
+
agent_name: str | None = None,
|
|
2066
|
+
nonce: int | None = None,
|
|
2067
|
+
) -> dict:
|
|
2068
|
+
"""Одобрение API-кошелька.
|
|
2069
|
+
|
|
2070
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#approve-an-api-wallet
|
|
2071
|
+
"""
|
|
2072
|
+
if self._wallet is None:
|
|
2073
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
2074
|
+
|
|
2075
|
+
action: dict[str, Any] = {
|
|
2076
|
+
"type": "approveAgent",
|
|
2077
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
2078
|
+
"signatureChainId": signature_chain_id,
|
|
2079
|
+
"agentAddress": agent_address,
|
|
2080
|
+
"nonce": nonce_value,
|
|
2081
|
+
}
|
|
2082
|
+
if agent_name is not None:
|
|
2083
|
+
action["agentName"] = agent_name
|
|
2084
|
+
else:
|
|
2085
|
+
action["agentName"] = ""
|
|
2086
|
+
action_nonce = nonce if nonce is not None else nonce_value
|
|
2087
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
2088
|
+
signature = _sign_user_signed_action(
|
|
2089
|
+
self._wallet,
|
|
2090
|
+
action,
|
|
2091
|
+
APPROVE_AGENT_SIGN_TYPES,
|
|
2092
|
+
"HyperliquidTransaction:ApproveAgent",
|
|
2093
|
+
is_mainnet,
|
|
2094
|
+
)
|
|
2095
|
+
|
|
2096
|
+
payload = {
|
|
2097
|
+
"action": action,
|
|
2098
|
+
"nonce": action_nonce,
|
|
2099
|
+
"signature": signature,
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
return await self._post_request("/exchange", data=payload)
|
|
2103
|
+
|
|
2104
|
+
async def approve_builder_fee(
|
|
2105
|
+
self,
|
|
2106
|
+
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
2107
|
+
signature_chain_id: str,
|
|
2108
|
+
max_fee_rate: str,
|
|
2109
|
+
builder_address: str,
|
|
2110
|
+
nonce_value: int,
|
|
2111
|
+
nonce: int | None = None,
|
|
2112
|
+
) -> dict:
|
|
2113
|
+
"""Одобрение максимальной комиссии билдера.
|
|
2114
|
+
|
|
2115
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#approve-a-builder-fee
|
|
2116
|
+
"""
|
|
2117
|
+
if self._wallet is None:
|
|
2118
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
2119
|
+
|
|
2120
|
+
action = {
|
|
2121
|
+
"type": "approveBuilderFee",
|
|
2122
|
+
"hyperliquidChain": hyperliquid_chain,
|
|
2123
|
+
"signatureChainId": signature_chain_id,
|
|
2124
|
+
"maxFeeRate": max_fee_rate,
|
|
2125
|
+
"builder": builder_address,
|
|
2126
|
+
"nonce": nonce_value,
|
|
2127
|
+
}
|
|
2128
|
+
action_nonce = nonce if nonce is not None else nonce_value
|
|
2129
|
+
is_mainnet = hyperliquid_chain == "Mainnet"
|
|
2130
|
+
signature = _sign_user_signed_action(
|
|
2131
|
+
self._wallet,
|
|
2132
|
+
action,
|
|
2133
|
+
APPROVE_BUILDER_FEE_SIGN_TYPES,
|
|
2134
|
+
"HyperliquidTransaction:ApproveBuilderFee",
|
|
2135
|
+
is_mainnet,
|
|
2136
|
+
)
|
|
2137
|
+
|
|
2138
|
+
payload = {
|
|
2139
|
+
"action": action,
|
|
2140
|
+
"nonce": action_nonce,
|
|
2141
|
+
"signature": signature,
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
return await self._post_request("/exchange", data=payload)
|
|
2145
|
+
|
|
2146
|
+
async def place_twap_order(
|
|
2147
|
+
self,
|
|
2148
|
+
asset: int,
|
|
2149
|
+
is_buy: bool,
|
|
2150
|
+
size: str | float,
|
|
2151
|
+
reduce_only: bool,
|
|
2152
|
+
minutes: int,
|
|
2153
|
+
randomize: bool,
|
|
2154
|
+
nonce: int | None = None,
|
|
2155
|
+
expires_after: int | None = None,
|
|
2156
|
+
vault_address: str | None = None,
|
|
2157
|
+
) -> dict:
|
|
2158
|
+
"""Создание TWAP-ордера.
|
|
2159
|
+
|
|
2160
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-a-twap-order
|
|
2161
|
+
"""
|
|
2162
|
+
if self._wallet is None:
|
|
2163
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
2164
|
+
|
|
2165
|
+
action = {
|
|
2166
|
+
"type": "twapOrder",
|
|
2167
|
+
"twap": {
|
|
2168
|
+
"a": asset,
|
|
2169
|
+
"b": is_buy,
|
|
2170
|
+
"s": str(size),
|
|
2171
|
+
"r": reduce_only,
|
|
2172
|
+
"m": minutes,
|
|
2173
|
+
"t": randomize,
|
|
2174
|
+
},
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
effective_vault = vault_address or self._vault_address
|
|
2178
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
2179
|
+
signature = _sign_l1_action(
|
|
2180
|
+
self._wallet,
|
|
2181
|
+
action,
|
|
2182
|
+
effective_vault,
|
|
2183
|
+
action_nonce,
|
|
2184
|
+
expires_after,
|
|
2185
|
+
)
|
|
2186
|
+
|
|
2187
|
+
payload: dict[str, Any] = {
|
|
2188
|
+
"action": action,
|
|
2189
|
+
"nonce": action_nonce,
|
|
2190
|
+
"signature": signature,
|
|
2191
|
+
}
|
|
2192
|
+
if effective_vault is not None:
|
|
2193
|
+
payload["vaultAddress"] = effective_vault
|
|
2194
|
+
if expires_after is not None:
|
|
2195
|
+
payload["expiresAfter"] = expires_after
|
|
2196
|
+
|
|
2197
|
+
return await self._post_request("/exchange", data=payload)
|
|
2198
|
+
|
|
2199
|
+
async def cancel_twap_order(
|
|
2200
|
+
self,
|
|
2201
|
+
asset: int,
|
|
2202
|
+
twap_id: int,
|
|
2203
|
+
nonce: int | None = None,
|
|
2204
|
+
expires_after: int | None = None,
|
|
2205
|
+
vault_address: str | None = None,
|
|
2206
|
+
) -> dict:
|
|
2207
|
+
"""Отмена TWAP-ордера.
|
|
2208
|
+
|
|
2209
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-a-twap-order
|
|
2210
|
+
"""
|
|
2211
|
+
if self._wallet is None:
|
|
2212
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
2213
|
+
|
|
2214
|
+
action = {
|
|
2215
|
+
"type": "twapCancel",
|
|
2216
|
+
"a": asset,
|
|
2217
|
+
"t": twap_id,
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
effective_vault = vault_address or self._vault_address
|
|
2221
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
2222
|
+
signature = _sign_l1_action(
|
|
2223
|
+
self._wallet,
|
|
2224
|
+
action,
|
|
2225
|
+
effective_vault,
|
|
2226
|
+
action_nonce,
|
|
2227
|
+
expires_after,
|
|
2228
|
+
)
|
|
2229
|
+
|
|
2230
|
+
payload: dict[str, Any] = {
|
|
2231
|
+
"action": action,
|
|
2232
|
+
"nonce": action_nonce,
|
|
2233
|
+
"signature": signature,
|
|
2234
|
+
}
|
|
2235
|
+
if effective_vault is not None:
|
|
2236
|
+
payload["vaultAddress"] = effective_vault
|
|
2237
|
+
if expires_after is not None:
|
|
2238
|
+
payload["expiresAfter"] = expires_after
|
|
2239
|
+
|
|
2240
|
+
return await self._post_request("/exchange", data=payload)
|
|
2241
|
+
|
|
2242
|
+
async def reserve_request_weight(
|
|
2243
|
+
self,
|
|
2244
|
+
weight: int,
|
|
2245
|
+
nonce: int | None = None,
|
|
2246
|
+
expires_after: int | None = None,
|
|
2247
|
+
vault_address: str | None = None,
|
|
2248
|
+
) -> dict:
|
|
2249
|
+
"""Резервирование дополнительного лимита действий.
|
|
2250
|
+
|
|
2251
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#reserve-additional-actions
|
|
2252
|
+
"""
|
|
2253
|
+
if self._wallet is None:
|
|
2254
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
2255
|
+
|
|
2256
|
+
action = {"type": "reserveRequestWeight", "weight": weight}
|
|
2257
|
+
|
|
2258
|
+
effective_vault = vault_address or self._vault_address
|
|
2259
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
2260
|
+
signature = _sign_l1_action(
|
|
2261
|
+
self._wallet,
|
|
2262
|
+
action,
|
|
2263
|
+
effective_vault,
|
|
2264
|
+
action_nonce,
|
|
2265
|
+
expires_after,
|
|
2266
|
+
)
|
|
2267
|
+
|
|
2268
|
+
payload: dict[str, Any] = {
|
|
2269
|
+
"action": action,
|
|
2270
|
+
"nonce": action_nonce,
|
|
2271
|
+
"signature": signature,
|
|
2272
|
+
}
|
|
2273
|
+
if effective_vault is not None:
|
|
2274
|
+
payload["vaultAddress"] = effective_vault
|
|
2275
|
+
if expires_after is not None:
|
|
2276
|
+
payload["expiresAfter"] = expires_after
|
|
2277
|
+
|
|
2278
|
+
return await self._post_request("/exchange", data=payload)
|
|
2279
|
+
|
|
2280
|
+
async def invalidate_pending_nonce(
|
|
2281
|
+
self,
|
|
2282
|
+
nonce: int | None = None,
|
|
2283
|
+
expires_after: int | None = None,
|
|
2284
|
+
vault_address: str | None = None,
|
|
2285
|
+
) -> dict:
|
|
2286
|
+
"""Инвалидация ожидающего nonce без выполнения действия.
|
|
2287
|
+
|
|
2288
|
+
https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#invalidate-pending-nonce-noop
|
|
2289
|
+
"""
|
|
2290
|
+
if self._wallet is None:
|
|
2291
|
+
raise NotAuthorized("Private key is required for private endpoints.")
|
|
2292
|
+
|
|
2293
|
+
action = {"type": "noop"}
|
|
2294
|
+
|
|
2295
|
+
effective_vault = vault_address or self._vault_address
|
|
2296
|
+
action_nonce = nonce if nonce is not None else int(time.time() * 1000)
|
|
2297
|
+
signature = _sign_l1_action(
|
|
2298
|
+
self._wallet,
|
|
2299
|
+
action,
|
|
2300
|
+
effective_vault,
|
|
2301
|
+
action_nonce,
|
|
2302
|
+
expires_after,
|
|
2303
|
+
)
|
|
2304
|
+
|
|
2305
|
+
payload: dict[str, Any] = {
|
|
2306
|
+
"action": action,
|
|
2307
|
+
"nonce": action_nonce,
|
|
2308
|
+
"signature": signature,
|
|
2309
|
+
}
|
|
2310
|
+
if effective_vault is not None:
|
|
2311
|
+
payload["vaultAddress"] = effective_vault
|
|
2312
|
+
if expires_after is not None:
|
|
2313
|
+
payload["expiresAfter"] = expires_after
|
|
2314
|
+
|
|
2315
|
+
return await self._post_request("/exchange", data=payload)
|