async-hyperliquid 0.4.3__tar.gz → 0.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/PKG-INFO +2 -1
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/pyproject.toml +2 -1
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/_async_hyperliquid/actions.py +23 -45
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/_async_hyperliquid/core.py +76 -39
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/_async_hyperliquid/info.py +16 -28
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/_async_hyperliquid/orders.py +71 -64
- async_hyperliquid-0.4.4/src/async_hyperliquid/async_api.py +85 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/exchange.py +14 -7
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/info.py +5 -16
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/signing.py +111 -70
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/types.py +1 -5
- async_hyperliquid-0.4.3/src/async_hyperliquid/async_api.py +0 -51
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/LICENSE +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/README.md +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/__init__.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/_async_hyperliquid/__init__.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/async_hyperliquid.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/__init__.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/constants.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/decorators.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/miscs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: async-hyperliquid
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Async Hyperliquid client using aiohttp
|
|
5
5
|
Keywords: dex,hyperliquid,async,aiohttp,trading,cryptocurrency,defi
|
|
6
6
|
Author: Yuki
|
|
@@ -48,6 +48,7 @@ Requires-Dist: msgpack>=1.1.0,<2.0.0
|
|
|
48
48
|
Requires-Dist: eth-account>=0.13.5,<0.14.0
|
|
49
49
|
Requires-Dist: eth-utils>=5.2.0,<6.0.0
|
|
50
50
|
Requires-Dist: hl-web3>=0.1.0
|
|
51
|
+
Requires-Dist: coincurve>=21.0.0
|
|
51
52
|
Requires-Python: >=3.10, <4
|
|
52
53
|
Project-URL: Changelog, https://github.com/traderfiapp/async-hyperliquid/blob/master/CHANGELOG.md
|
|
53
54
|
Project-URL: Documentation, https://github.com/traderfiapp/async-hyperliquid
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "async-hyperliquid"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.4"
|
|
4
4
|
description = "Async Hyperliquid client using aiohttp"
|
|
5
5
|
authors = [{ name = "Yuki", email = "yuqi.lyle@gmail.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -40,6 +40,7 @@ dependencies = [
|
|
|
40
40
|
"eth-account (>=0.13.5,<0.14.0)",
|
|
41
41
|
"eth-utils (>=5.2.0,<6.0.0)",
|
|
42
42
|
"hl-web3>=0.1.0",
|
|
43
|
+
"coincurve>=21.0.0",
|
|
43
44
|
]
|
|
44
45
|
|
|
45
46
|
[dependency-groups]
|
|
@@ -2,13 +2,9 @@ import math
|
|
|
2
2
|
import re
|
|
3
3
|
import warnings
|
|
4
4
|
|
|
5
|
-
from async_hyperliquid.utils.constants import
|
|
6
|
-
HYPE_FACTOR,
|
|
7
|
-
MAINNET_API_URL,
|
|
8
|
-
USD_FACTOR,
|
|
9
|
-
)
|
|
5
|
+
from async_hyperliquid.utils.constants import HYPE_FACTOR, MAINNET_API_URL, USD_FACTOR
|
|
10
6
|
from async_hyperliquid.utils.decorators import private_key_required
|
|
11
|
-
from async_hyperliquid.utils.miscs import
|
|
7
|
+
from async_hyperliquid.utils.miscs import round_token_amount
|
|
12
8
|
from async_hyperliquid.utils.signing import (
|
|
13
9
|
sign_approve_agent_action,
|
|
14
10
|
sign_approve_builder_fee_action,
|
|
@@ -41,7 +37,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
41
37
|
|
|
42
38
|
@private_key_required
|
|
43
39
|
async def usd_transfer(self, amount: float, dest: str):
|
|
44
|
-
nonce =
|
|
40
|
+
nonce = self.next_nonce()
|
|
45
41
|
action = {
|
|
46
42
|
"type": "usdSend",
|
|
47
43
|
"amount": round_token_amount(amount, 2),
|
|
@@ -59,7 +55,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
59
55
|
token_id = token_info["tokenId"]
|
|
60
56
|
wei_decimals = token_info["weiDecimals"]
|
|
61
57
|
token = f"{token_name}:{token_id}"
|
|
62
|
-
nonce =
|
|
58
|
+
nonce = self.next_nonce()
|
|
63
59
|
action = {
|
|
64
60
|
"type": "spotSend",
|
|
65
61
|
"destination": dest,
|
|
@@ -72,7 +68,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
72
68
|
|
|
73
69
|
@private_key_required
|
|
74
70
|
async def initiate_withdrawal(self, amount: float):
|
|
75
|
-
nonce =
|
|
71
|
+
nonce = self.next_nonce()
|
|
76
72
|
action = {
|
|
77
73
|
"type": "withdraw3",
|
|
78
74
|
"amount": round_token_amount(amount, 2),
|
|
@@ -84,7 +80,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
84
80
|
|
|
85
81
|
@private_key_required
|
|
86
82
|
async def usd_class_transfer(self, amount: float, to_perp: bool = False):
|
|
87
|
-
nonce =
|
|
83
|
+
nonce = self.next_nonce()
|
|
88
84
|
action = {
|
|
89
85
|
"type": "usdClassTransfer",
|
|
90
86
|
"amount": round_token_amount(amount, 2),
|
|
@@ -111,7 +107,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
111
107
|
token_id = token_info["tokenId"]
|
|
112
108
|
wei_decimals = token_info["weiDecimals"]
|
|
113
109
|
token = f"{token_name}:{token_id}"
|
|
114
|
-
nonce =
|
|
110
|
+
nonce = self.next_nonce()
|
|
115
111
|
action = {
|
|
116
112
|
"type": "sendAsset",
|
|
117
113
|
"token": token,
|
|
@@ -128,7 +124,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
128
124
|
@private_key_required
|
|
129
125
|
async def staking_deposit(self, amount: float):
|
|
130
126
|
amount_in_wei = int(math.floor(amount * HYPE_FACTOR))
|
|
131
|
-
nonce =
|
|
127
|
+
nonce = self.next_nonce()
|
|
132
128
|
action = {"type": "cDeposit", "wei": amount_in_wei, "nonce": nonce}
|
|
133
129
|
sig = sign_staking_deposit_action(self.account, action, self.is_mainnet)
|
|
134
130
|
return await self.exchange.post_action_with_sig(action, sig, nonce)
|
|
@@ -136,11 +132,9 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
136
132
|
@private_key_required
|
|
137
133
|
async def staking_withdraw(self, amount: float):
|
|
138
134
|
amount_in_wei = int(math.floor(amount * HYPE_FACTOR))
|
|
139
|
-
nonce =
|
|
135
|
+
nonce = self.next_nonce()
|
|
140
136
|
action = {"type": "cWithdraw", "wei": amount_in_wei, "nonce": nonce}
|
|
141
|
-
sig = sign_staking_withdraw_action(
|
|
142
|
-
self.account, action, self.is_mainnet
|
|
143
|
-
)
|
|
137
|
+
sig = sign_staking_withdraw_action(self.account, action, self.is_mainnet)
|
|
144
138
|
return await self.exchange.post_action_with_sig(action, sig, nonce)
|
|
145
139
|
|
|
146
140
|
@private_key_required
|
|
@@ -148,7 +142,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
148
142
|
self, validator: str, amount: float, is_undelegate: bool = False
|
|
149
143
|
):
|
|
150
144
|
amount_in_wei = int(math.floor(amount * HYPE_FACTOR))
|
|
151
|
-
nonce =
|
|
145
|
+
nonce = self.next_nonce()
|
|
152
146
|
action = {
|
|
153
147
|
"type": "tokenDelegate",
|
|
154
148
|
"validator": validator,
|
|
@@ -160,9 +154,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
160
154
|
return await self.exchange.post_action_with_sig(action, sig, nonce)
|
|
161
155
|
|
|
162
156
|
@private_key_required
|
|
163
|
-
async def vault_transfer(
|
|
164
|
-
self, vault: str, amount: float, is_deposit: bool = True
|
|
165
|
-
):
|
|
157
|
+
async def vault_transfer(self, vault: str, amount: float, is_deposit: bool = True):
|
|
166
158
|
usd_amount = int(math.floor(amount * USD_FACTOR))
|
|
167
159
|
action = {
|
|
168
160
|
"type": "vaultTransfer",
|
|
@@ -173,7 +165,7 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
173
165
|
return await self.exchange.post_action(action)
|
|
174
166
|
|
|
175
167
|
async def approve_agent(self, agent: str, name: str | None = None):
|
|
176
|
-
nonce =
|
|
168
|
+
nonce = self.next_nonce()
|
|
177
169
|
action = {
|
|
178
170
|
"type": "approveAgent",
|
|
179
171
|
"agentAddress": agent,
|
|
@@ -187,26 +179,20 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
187
179
|
return await self.exchange.post_action_with_sig(action, sig, nonce)
|
|
188
180
|
|
|
189
181
|
async def approve_builder_fee(self, max_fee_rate: float, builder: str):
|
|
190
|
-
nonce =
|
|
182
|
+
nonce = self.next_nonce()
|
|
191
183
|
action = {
|
|
192
184
|
"type": "approveBuilderFee",
|
|
193
185
|
"maxFeeRate": f"{max_fee_rate:.3%}",
|
|
194
186
|
"builder": builder,
|
|
195
187
|
"nonce": nonce,
|
|
196
188
|
}
|
|
197
|
-
sig = sign_approve_builder_fee_action(
|
|
198
|
-
self.account, action, self.is_mainnet
|
|
199
|
-
)
|
|
189
|
+
sig = sign_approve_builder_fee_action(self.account, action, self.is_mainnet)
|
|
200
190
|
return await self.exchange.post_action_with_sig(action, sig, nonce)
|
|
201
191
|
|
|
202
192
|
async def convert_to_multi_sig_user(self, users: list[str], threshold: int):
|
|
203
|
-
nonce =
|
|
193
|
+
nonce = self.next_nonce()
|
|
204
194
|
signers = {"authorizedUsers": sorted(users), "threshold": threshold}
|
|
205
|
-
action = {
|
|
206
|
-
"type": "convertToMultiSigUser",
|
|
207
|
-
"signers": signers,
|
|
208
|
-
"nonce": nonce,
|
|
209
|
-
}
|
|
195
|
+
action = {"type": "convertToMultiSigUser", "signers": signers, "nonce": nonce}
|
|
210
196
|
sig = sign_convert_to_multi_sig_user_action(
|
|
211
197
|
self.account, action, self.is_mainnet
|
|
212
198
|
)
|
|
@@ -220,16 +206,14 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
220
206
|
action = {"type": "evmUserModify", "usingBigBlocks": enable}
|
|
221
207
|
return await self.exchange.post_action(action)
|
|
222
208
|
|
|
223
|
-
async def user_dex_abstraction(
|
|
224
|
-
self, user: str | None = None, enabled: bool = True
|
|
225
|
-
):
|
|
209
|
+
async def user_dex_abstraction(self, user: str | None = None, enabled: bool = True):
|
|
226
210
|
warnings.warn(
|
|
227
211
|
"user_dex_abstraction is deprecated and may be removed in a "
|
|
228
212
|
"future release.",
|
|
229
213
|
DeprecationWarning,
|
|
230
214
|
stacklevel=2,
|
|
231
215
|
)
|
|
232
|
-
nonce =
|
|
216
|
+
nonce = self.next_nonce()
|
|
233
217
|
if user is None:
|
|
234
218
|
user = self.address
|
|
235
219
|
action = {
|
|
@@ -238,30 +222,24 @@ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
|
|
|
238
222
|
"enabled": enabled,
|
|
239
223
|
"nonce": nonce,
|
|
240
224
|
}
|
|
241
|
-
sig = sign_user_dex_abstraction_action(
|
|
242
|
-
self.account, action, self.is_mainnet
|
|
243
|
-
)
|
|
225
|
+
sig = sign_user_dex_abstraction_action(self.account, action, self.is_mainnet)
|
|
244
226
|
return await self.exchange.post_action_with_sig(action, sig, nonce)
|
|
245
227
|
|
|
246
228
|
async def user_set_abstraction(
|
|
247
229
|
self, abstraction: UserSetAbstraction, user: str | None = None
|
|
248
230
|
):
|
|
249
|
-
nonce = get_timestamp_ms()
|
|
250
231
|
if user is None:
|
|
251
232
|
user = self.address
|
|
252
233
|
if re.fullmatch(r"0x[a-fA-F0-9]{40}", user) is None:
|
|
253
|
-
raise ValueError(
|
|
254
|
-
|
|
255
|
-
)
|
|
234
|
+
raise ValueError(f"user must be a 42-char hex address, got: {user!r}")
|
|
235
|
+
nonce = self.next_nonce()
|
|
256
236
|
action = {
|
|
257
237
|
"type": "userSetAbstraction",
|
|
258
238
|
"user": user.lower(),
|
|
259
239
|
"abstraction": abstraction,
|
|
260
240
|
"nonce": nonce,
|
|
261
241
|
}
|
|
262
|
-
sig = sign_user_set_abstraction_action(
|
|
263
|
-
self.account, action, self.is_mainnet
|
|
264
|
-
)
|
|
242
|
+
sig = sign_user_set_abstraction_action(self.account, action, self.is_mainnet)
|
|
265
243
|
return await self.exchange.post_action_with_sig(action, sig, nonce)
|
|
266
244
|
|
|
267
245
|
async def agent_enable_dex_abstraction(self):
|
{async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/_async_hyperliquid/core.py
RENAMED
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from threading import Lock
|
|
2
3
|
|
|
3
|
-
from aiohttp import ClientSession, ClientTimeout
|
|
4
|
+
from aiohttp import TCPConnector, BaseConnector, ClientSession, ClientTimeout
|
|
4
5
|
from eth_account import Account
|
|
5
|
-
from eth_account.signers.local import LocalAccount
|
|
6
|
-
from hl_web3.exchange import Exchange as EVMExchange
|
|
7
6
|
from hl_web3.info import Info as EVMInfo
|
|
7
|
+
from hl_web3.exchange import Exchange as EVMExchange
|
|
8
8
|
from hl_web3.utils.constants import HL_RPC_URL, HL_TESTNET_RPC_URL
|
|
9
|
+
from eth_account.signers.local import LocalAccount
|
|
9
10
|
|
|
10
|
-
from async_hyperliquid.async_api import AsyncAPI
|
|
11
|
-
from async_hyperliquid.exchange import ExchangeAPI
|
|
12
11
|
from async_hyperliquid.info import InfoAPI
|
|
12
|
+
from async_hyperliquid.exchange import ExchangeAPI
|
|
13
|
+
from async_hyperliquid.async_api import AsyncAPI
|
|
14
|
+
from async_hyperliquid.utils.miscs import get_timestamp_ms
|
|
15
|
+
from async_hyperliquid.utils.types import Metas, PerpMeta, SpotMeta, SpotTokenMeta
|
|
13
16
|
from async_hyperliquid.utils.constants import (
|
|
17
|
+
SPOT_OFFSET,
|
|
14
18
|
MAINNET_API_URL,
|
|
15
19
|
PERP_DEX_OFFSET,
|
|
16
|
-
SPOT_OFFSET,
|
|
17
20
|
TESTNET_API_URL,
|
|
18
21
|
)
|
|
19
|
-
from async_hyperliquid.utils.types import (
|
|
20
|
-
Metas,
|
|
21
|
-
PerpMeta,
|
|
22
|
-
SpotMeta,
|
|
23
|
-
SpotTokenMeta,
|
|
24
|
-
)
|
|
25
22
|
|
|
26
23
|
|
|
27
24
|
class AsyncHyperliquidCore(AsyncAPI):
|
|
@@ -54,15 +51,27 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
54
51
|
private_key: str | None = None,
|
|
55
52
|
vault: str | None = None,
|
|
56
53
|
perp_dexs: list[str] = [""],
|
|
54
|
+
session: ClientSession | None = None,
|
|
55
|
+
timeout: ClientTimeout | None = None,
|
|
56
|
+
connector: BaseConnector | None = None,
|
|
57
57
|
):
|
|
58
58
|
self.address = address
|
|
59
59
|
self.is_mainnet = is_mainnet
|
|
60
60
|
self.account = Account.from_key(api_key)
|
|
61
|
-
self.
|
|
61
|
+
self._nonce_lock = Lock()
|
|
62
|
+
self._last_nonce = 0
|
|
63
|
+
self._owns_session = session is None
|
|
64
|
+
self.session = session or self._build_session(
|
|
65
|
+
timeout=timeout, connector=connector
|
|
66
|
+
)
|
|
62
67
|
self.base_url = MAINNET_API_URL if is_mainnet else TESTNET_API_URL
|
|
63
68
|
self.info = InfoAPI(self.base_url, self.session)
|
|
64
69
|
self.exchange = ExchangeAPI(
|
|
65
|
-
self.account,
|
|
70
|
+
self.account,
|
|
71
|
+
self.session,
|
|
72
|
+
self.base_url,
|
|
73
|
+
address=self.address,
|
|
74
|
+
nonce_factory=self.next_nonce,
|
|
66
75
|
)
|
|
67
76
|
|
|
68
77
|
self.coin_assets = {}
|
|
@@ -81,6 +90,25 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
81
90
|
def set_expires(self, expires: int | None) -> None:
|
|
82
91
|
self.expires = expires
|
|
83
92
|
|
|
93
|
+
def _build_session(
|
|
94
|
+
self, *, timeout: ClientTimeout | None, connector: BaseConnector | None
|
|
95
|
+
) -> ClientSession:
|
|
96
|
+
resolved_timeout = timeout or ClientTimeout(
|
|
97
|
+
connect=3, sock_connect=3, sock_read=10
|
|
98
|
+
)
|
|
99
|
+
resolved_connector = connector or TCPConnector(
|
|
100
|
+
ttl_dns_cache=300, enable_cleanup_closed=True
|
|
101
|
+
)
|
|
102
|
+
return ClientSession(timeout=resolved_timeout, connector=resolved_connector)
|
|
103
|
+
|
|
104
|
+
def next_nonce(self) -> int:
|
|
105
|
+
with self._nonce_lock:
|
|
106
|
+
nonce = get_timestamp_ms()
|
|
107
|
+
if nonce <= self._last_nonce:
|
|
108
|
+
nonce = self._last_nonce + 1
|
|
109
|
+
self._last_nonce = nonce
|
|
110
|
+
return nonce
|
|
111
|
+
|
|
84
112
|
def _init_evm_client(
|
|
85
113
|
self, private_key: str | None, rpc_url: str | None = None
|
|
86
114
|
) -> None:
|
|
@@ -91,9 +119,7 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
91
119
|
|
|
92
120
|
if private_key is None:
|
|
93
121
|
if self.account.address != self.address:
|
|
94
|
-
raise ValueError(
|
|
95
|
-
"EVM Exchange client can not init without private key"
|
|
96
|
-
)
|
|
122
|
+
raise ValueError("EVM Exchange client can not init without private key")
|
|
97
123
|
private_key = self.account.key.hex()
|
|
98
124
|
|
|
99
125
|
self.evm_exchange = EVMExchange(rpc_url, private_key)
|
|
@@ -107,7 +133,8 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
107
133
|
self.asset_sz_decimals[asset] = info["szDecimals"]
|
|
108
134
|
|
|
109
135
|
def _init_spot_meta(self, meta: SpotMeta) -> None:
|
|
110
|
-
|
|
136
|
+
tokens = meta["tokens"]
|
|
137
|
+
total_tokens = len(tokens)
|
|
111
138
|
for info in meta["universe"]:
|
|
112
139
|
asset = info["index"] + SPOT_OFFSET
|
|
113
140
|
asset_name = info["name"]
|
|
@@ -116,19 +143,20 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
116
143
|
self.coin_names[asset_name] = asset_name
|
|
117
144
|
|
|
118
145
|
base, quote = info["tokens"]
|
|
119
|
-
if base
|
|
120
|
-
print("Unreconized token index for: ", info)
|
|
146
|
+
if not 0 <= base < total_tokens or not 0 <= quote < total_tokens:
|
|
121
147
|
continue
|
|
122
148
|
|
|
123
|
-
base_info =
|
|
149
|
+
base_info = tokens[base]
|
|
124
150
|
base_name = base_info["name"]
|
|
125
|
-
|
|
151
|
+
quote_info = tokens[quote]
|
|
152
|
+
quote_name = quote_info["name"]
|
|
126
153
|
name = f"{base_name}/{quote_name}"
|
|
127
|
-
|
|
128
|
-
|
|
154
|
+
self.coin_names.setdefault(name, asset_name)
|
|
155
|
+
self.coin_names.setdefault(quote_name, quote_name)
|
|
129
156
|
|
|
130
157
|
self.asset_sz_decimals[asset] = base_info["szDecimals"]
|
|
131
|
-
self.spot_tokens[asset_name] =
|
|
158
|
+
self.spot_tokens[asset_name] = base_info
|
|
159
|
+
self.spot_tokens.setdefault(quote_name, quote_info)
|
|
132
160
|
|
|
133
161
|
def _update_coin_symbols(self) -> None:
|
|
134
162
|
self.coin_symbols = {
|
|
@@ -171,24 +199,31 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
171
199
|
|
|
172
200
|
async def get_metas(self, perp_only: bool = False) -> Metas:
|
|
173
201
|
metas: Metas = {"perp": {}, "spot": [], "dexs": {}} # type: ignore
|
|
174
|
-
perp_meta = await self.info.get_perp_meta()
|
|
175
202
|
if perp_only:
|
|
176
|
-
metas["perp"] =
|
|
203
|
+
metas["perp"] = await self.info.get_perp_meta()
|
|
177
204
|
return metas
|
|
178
205
|
|
|
179
|
-
|
|
206
|
+
perp_meta, spot_meta = await asyncio.gather(
|
|
207
|
+
self.info.get_perp_meta(), self.info.get_spot_meta()
|
|
208
|
+
)
|
|
209
|
+
metas["perp"] = perp_meta
|
|
210
|
+
metas["spot"] = spot_meta
|
|
180
211
|
return metas
|
|
181
212
|
|
|
182
213
|
async def get_all_metas(self) -> Metas:
|
|
183
|
-
dexs = await
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
214
|
+
dexs, perp_meta, spot_meta = await asyncio.gather(
|
|
215
|
+
self.get_all_dex_name(),
|
|
216
|
+
self.info.get_perp_meta(),
|
|
217
|
+
self.info.get_spot_meta(),
|
|
218
|
+
)
|
|
219
|
+
dex_metas: dict[str, PerpMeta] = {}
|
|
220
|
+
if len(dexs) > 1:
|
|
221
|
+
dex_meta_results = await asyncio.gather(
|
|
222
|
+
*(self.info.get_perp_meta(dex) for dex in dexs[1:])
|
|
223
|
+
)
|
|
224
|
+
dex_metas = {
|
|
225
|
+
dex: meta for dex, meta in zip(dexs[1:], dex_meta_results, strict=True)
|
|
226
|
+
}
|
|
192
227
|
return {"perp": perp_meta, "spot": spot_meta, "dexs": dex_metas}
|
|
193
228
|
|
|
194
229
|
async def get_all_dex_name(self) -> list[str]:
|
|
@@ -212,7 +247,6 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
212
247
|
|
|
213
248
|
async def get_coin_asset(self, coin: str) -> int:
|
|
214
249
|
coin_name = await self.get_coin_name(coin)
|
|
215
|
-
|
|
216
250
|
if coin_name not in self.coin_assets:
|
|
217
251
|
raise ValueError(f"Coin {coin}({coin_name}) not found")
|
|
218
252
|
|
|
@@ -224,7 +258,10 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
224
258
|
|
|
225
259
|
async def get_coin_sz_decimals(self, coin: str) -> int:
|
|
226
260
|
coin_name = await self.get_coin_name(coin)
|
|
227
|
-
|
|
261
|
+
if coin_name not in self.coin_assets:
|
|
262
|
+
raise ValueError(f"Coin {coin}({coin_name}) not found")
|
|
263
|
+
|
|
264
|
+
asset = self.coin_assets[coin_name]
|
|
228
265
|
return self.asset_sz_decimals[asset]
|
|
229
266
|
|
|
230
267
|
async def get_token_info(self, coin: str) -> SpotTokenMeta:
|
{async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/_async_hyperliquid/info.py
RENAMED
|
@@ -2,11 +2,7 @@ import asyncio
|
|
|
2
2
|
import warnings
|
|
3
3
|
from typing import Literal
|
|
4
4
|
|
|
5
|
-
from async_hyperliquid.utils.constants import
|
|
6
|
-
ONE_HOUR_MS,
|
|
7
|
-
PERP_DEX_OFFSET,
|
|
8
|
-
SPOT_OFFSET,
|
|
9
|
-
)
|
|
5
|
+
from async_hyperliquid.utils.constants import ONE_HOUR_MS, PERP_DEX_OFFSET, SPOT_OFFSET
|
|
10
6
|
from async_hyperliquid.utils.miscs import get_coin_dex, get_timestamp_ms
|
|
11
7
|
from async_hyperliquid.utils.types import (
|
|
12
8
|
Abstraction,
|
|
@@ -50,10 +46,15 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
50
46
|
await self.init_metas()
|
|
51
47
|
spot_data = None
|
|
52
48
|
perp_data = None
|
|
53
|
-
if
|
|
54
|
-
spot_data = await
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
if is_all:
|
|
50
|
+
spot_data, perp_data = await asyncio.gather(
|
|
51
|
+
self.info.get_spot_meta_ctx(), self.info.get_perp_meta_ctx()
|
|
52
|
+
)
|
|
53
|
+
else:
|
|
54
|
+
if is_spot:
|
|
55
|
+
spot_data = await self.info.get_spot_meta_ctx()
|
|
56
|
+
if is_perp:
|
|
57
|
+
perp_data = await self.info.get_perp_meta_ctx()
|
|
57
58
|
|
|
58
59
|
is_perp = is_perp or is_all
|
|
59
60
|
is_spot = is_spot or is_all
|
|
@@ -102,9 +103,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
102
103
|
address = self.address
|
|
103
104
|
return await self.info.get_spot_clearinghouse_state(address)
|
|
104
105
|
|
|
105
|
-
async def get_account_state(
|
|
106
|
-
self, address: str | None = None
|
|
107
|
-
) -> AccountState:
|
|
106
|
+
async def get_account_state(self, address: str | None = None) -> AccountState:
|
|
108
107
|
if not address:
|
|
109
108
|
address = self.address
|
|
110
109
|
|
|
@@ -127,9 +126,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
127
126
|
|
|
128
127
|
return account_state
|
|
129
128
|
|
|
130
|
-
async def get_account_portfolio(
|
|
131
|
-
self, address: str | None = None
|
|
132
|
-
) -> Portfolio:
|
|
129
|
+
async def get_account_portfolio(self, address: str | None = None) -> Portfolio:
|
|
133
130
|
if not address:
|
|
134
131
|
address = self.address
|
|
135
132
|
|
|
@@ -158,9 +155,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
158
155
|
start_time: int | None = None,
|
|
159
156
|
end_time: int | None = None,
|
|
160
157
|
) -> list[UserDeposit]:
|
|
161
|
-
return await self.get_latest_ledgers(
|
|
162
|
-
"deposit", address, start_time, end_time
|
|
163
|
-
) # type: ignore
|
|
158
|
+
return await self.get_latest_ledgers("deposit", address, start_time, end_time) # type: ignore
|
|
164
159
|
|
|
165
160
|
async def get_latest_withdraws(
|
|
166
161
|
self,
|
|
@@ -168,9 +163,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
168
163
|
start_time: int | None = None,
|
|
169
164
|
end_time: int | None = None,
|
|
170
165
|
) -> list[UserWithdraw]:
|
|
171
|
-
return await self.get_latest_ledgers(
|
|
172
|
-
"withdraw", address, start_time, end_time
|
|
173
|
-
) # type: ignore
|
|
166
|
+
return await self.get_latest_ledgers("withdraw", address, start_time, end_time) # type: ignore
|
|
174
167
|
|
|
175
168
|
async def get_latest_transfers(
|
|
176
169
|
self,
|
|
@@ -183,10 +176,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
183
176
|
) # type: ignore
|
|
184
177
|
|
|
185
178
|
async def get_user_open_orders(
|
|
186
|
-
self,
|
|
187
|
-
address: str | None = None,
|
|
188
|
-
is_frontend: bool = False,
|
|
189
|
-
dex: str = "",
|
|
179
|
+
self, address: str | None = None, is_frontend: bool = False, dex: str = ""
|
|
190
180
|
) -> UserOpenOrders:
|
|
191
181
|
if not address:
|
|
192
182
|
address = self.address
|
|
@@ -226,9 +216,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
226
216
|
positions.extend(result)
|
|
227
217
|
return positions
|
|
228
218
|
|
|
229
|
-
async def get_user_abstraction(
|
|
230
|
-
self, address: str | None = None
|
|
231
|
-
) -> Abstraction:
|
|
219
|
+
async def get_user_abstraction(self, address: str | None = None) -> Abstraction:
|
|
232
220
|
if not address:
|
|
233
221
|
address = self.address
|
|
234
222
|
return await self.info.get_user_abstraction(address)
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import math
|
|
2
3
|
|
|
3
|
-
from async_hyperliquid.utils.constants import
|
|
4
|
-
PERP_DEX_OFFSET,
|
|
5
|
-
SPOT_OFFSET,
|
|
6
|
-
USD_FACTOR,
|
|
7
|
-
)
|
|
4
|
+
from async_hyperliquid.utils.constants import PERP_DEX_OFFSET, SPOT_OFFSET, USD_FACTOR
|
|
8
5
|
from async_hyperliquid.utils.miscs import get_coin_dex, round_float, round_px
|
|
9
6
|
from async_hyperliquid.utils.signing import encode_order, orders_to_action
|
|
10
7
|
from async_hyperliquid.utils.types import (
|
|
@@ -24,9 +21,13 @@ from .info import AsyncHyperliquidInfoClient
|
|
|
24
21
|
|
|
25
22
|
class AsyncHyperliquidOrdersClient(AsyncHyperliquidInfoClient):
|
|
26
23
|
async def _round_sz_px(self, coin: str, sz: float, px: float):
|
|
27
|
-
|
|
24
|
+
coin_name = await self.get_coin_name(coin)
|
|
25
|
+
if coin_name not in self.coin_assets:
|
|
26
|
+
raise ValueError(f"Coin {coin}({coin_name}) not found")
|
|
27
|
+
|
|
28
|
+
asset = self.coin_assets[coin_name]
|
|
29
|
+
sz_decimals = self.asset_sz_decimals[asset]
|
|
28
30
|
is_spot = asset >= SPOT_OFFSET and asset < PERP_DEX_OFFSET
|
|
29
|
-
sz_decimals = await self.get_coin_sz_decimals(coin)
|
|
30
31
|
px_decimals = (6 if not is_spot else 8) - sz_decimals
|
|
31
32
|
return asset, round_float(sz, sz_decimals), round_px(px, px_decimals)
|
|
32
33
|
|
|
@@ -146,41 +147,44 @@ class AsyncHyperliquidOrdersClient(AsyncHyperliquidInfoClient):
|
|
|
146
147
|
slippage: float = 0.05,
|
|
147
148
|
builder: OrderBuilder | None = None,
|
|
148
149
|
):
|
|
149
|
-
reqs = []
|
|
150
150
|
if is_market:
|
|
151
151
|
reqs = await self._get_batch_market_orders(orders, slippage)
|
|
152
152
|
else:
|
|
153
|
-
|
|
154
|
-
asset, sz, px = await self._round_sz_px(
|
|
155
|
-
o["coin"], o["sz"], o["px"]
|
|
156
|
-
)
|
|
157
|
-
req = {**o, "asset": asset, "sz": sz, "px": px}
|
|
158
|
-
reqs.append(req)
|
|
153
|
+
reqs = await self._get_batch_limit_orders(orders)
|
|
159
154
|
|
|
160
155
|
return await self.place_orders(reqs, grouping=grouping, builder=builder)
|
|
161
156
|
|
|
157
|
+
async def _get_batch_limit_orders(self, orders: BatchPlaceOrderRequest):
|
|
158
|
+
rounded_orders = await asyncio.gather(
|
|
159
|
+
*(self._round_sz_px(o["coin"], o["sz"], o["px"]) for o in orders)
|
|
160
|
+
)
|
|
161
|
+
return [
|
|
162
|
+
{**order, "asset": asset, "sz": sz, "px": px}
|
|
163
|
+
for order, (asset, sz, px) in zip(orders, rounded_orders)
|
|
164
|
+
]
|
|
165
|
+
|
|
162
166
|
async def _get_batch_market_orders(
|
|
163
167
|
self, orders: BatchPlaceOrderRequest, slippage: float = 0.05
|
|
164
168
|
):
|
|
165
|
-
reqs = []
|
|
166
169
|
dexs = list(set(get_coin_dex(o["coin"]) for o in orders))
|
|
167
170
|
all_mids = await self.get_dexs_mids(dexs)
|
|
168
171
|
order_type = limit_order_type(LimitTif.IOC)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
market_price = all_mids[coin]
|
|
172
|
-
slippage_factor = (1 + slippage) if
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
"
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
172
|
+
quoted_prices = []
|
|
173
|
+
for order in orders:
|
|
174
|
+
market_price = all_mids[order["coin"]]
|
|
175
|
+
slippage_factor = (1 + slippage) if order["is_buy"] else (1 - slippage)
|
|
176
|
+
quoted_prices.append(market_price * slippage_factor)
|
|
177
|
+
|
|
178
|
+
rounded_orders = await asyncio.gather(
|
|
179
|
+
*(
|
|
180
|
+
self._round_sz_px(order["coin"], order["sz"], quoted_price)
|
|
181
|
+
for order, quoted_price in zip(orders, quoted_prices)
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
return [
|
|
185
|
+
{**order, "asset": asset, "sz": sz, "px": px, "order_type": order_type}
|
|
186
|
+
for order, (asset, sz, px) in zip(orders, rounded_orders)
|
|
187
|
+
]
|
|
184
188
|
|
|
185
189
|
async def cancel_order(self, coin: str, oid: int):
|
|
186
190
|
return await self.cancel_orders([(coin, int(oid))])
|
|
@@ -189,11 +193,14 @@ class AsyncHyperliquidOrdersClient(AsyncHyperliquidInfoClient):
|
|
|
189
193
|
return await self.cancel_orders(cancels)
|
|
190
194
|
|
|
191
195
|
async def cancel_orders(self, cancels: BatchCancelRequest):
|
|
196
|
+
assets = await asyncio.gather(
|
|
197
|
+
*(self.get_coin_asset(coin) for coin, _ in cancels)
|
|
198
|
+
)
|
|
192
199
|
action = {
|
|
193
200
|
"type": "cancel",
|
|
194
201
|
"cancels": [
|
|
195
|
-
{"a":
|
|
196
|
-
for
|
|
202
|
+
{"a": asset, "o": oid}
|
|
203
|
+
for asset, (_, oid) in zip(assets, cancels, strict=True)
|
|
197
204
|
],
|
|
198
205
|
}
|
|
199
206
|
|
|
@@ -205,14 +212,14 @@ class AsyncHyperliquidOrdersClient(AsyncHyperliquidInfoClient):
|
|
|
205
212
|
return await self.batch_cancel_by_cloid([(coin, cloid)])
|
|
206
213
|
|
|
207
214
|
async def batch_cancel_by_cloid(self, cancels: list[tuple[str, Cloid]]):
|
|
215
|
+
assets = await asyncio.gather(
|
|
216
|
+
*(self.get_coin_asset(coin) for coin, _ in cancels)
|
|
217
|
+
)
|
|
208
218
|
action = {
|
|
209
219
|
"type": "cancelByCloid",
|
|
210
220
|
"cancels": [
|
|
211
|
-
{
|
|
212
|
-
|
|
213
|
-
"cloid": cloid.to_raw(),
|
|
214
|
-
}
|
|
215
|
-
for coin, cloid in cancels
|
|
221
|
+
{"asset": asset, "cloid": cloid.to_raw()}
|
|
222
|
+
for asset, (_, cloid) in zip(assets, cancels, strict=True)
|
|
216
223
|
],
|
|
217
224
|
}
|
|
218
225
|
|
|
@@ -255,9 +262,7 @@ class AsyncHyperliquidOrdersClient(AsyncHyperliquidInfoClient):
|
|
|
255
262
|
async def batch_modify_orders(self, modify_req: list[dict]):
|
|
256
263
|
modifies = [
|
|
257
264
|
{
|
|
258
|
-
"oid": m["oid"].to_raw()
|
|
259
|
-
if isinstance(m["oid"], Cloid)
|
|
260
|
-
else m["oid"],
|
|
265
|
+
"oid": m["oid"].to_raw() if isinstance(m["oid"], Cloid) else m["oid"],
|
|
261
266
|
"order": encode_order(m["order"]),
|
|
262
267
|
}
|
|
263
268
|
for m in modify_req
|
|
@@ -267,9 +272,7 @@ class AsyncHyperliquidOrdersClient(AsyncHyperliquidInfoClient):
|
|
|
267
272
|
action, vault=self.vault, expires=self.expires
|
|
268
273
|
)
|
|
269
274
|
|
|
270
|
-
async def update_leverage(
|
|
271
|
-
self, leverage: int, coin: str, is_cross: bool = True
|
|
272
|
-
):
|
|
275
|
+
async def update_leverage(self, leverage: int, coin: str, is_cross: bool = True):
|
|
273
276
|
action = {
|
|
274
277
|
"type": "updateLeverage",
|
|
275
278
|
"asset": await self.get_coin_asset(coin),
|
|
@@ -355,29 +358,33 @@ class AsyncHyperliquidOrdersClient(AsyncHyperliquidInfoClient):
|
|
|
355
358
|
async def close_dex_positions(self, dex: str):
|
|
356
359
|
return await self.close_all_positions(dexs=[dex])
|
|
357
360
|
|
|
358
|
-
async def
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
361
|
+
async def close_positions(self, coins: list[str]):
|
|
362
|
+
if not coins:
|
|
363
|
+
return None
|
|
364
|
+
|
|
365
|
+
positions = await self.get_all_positions(
|
|
366
|
+
dexs=sorted({get_coin_dex(coin) for coin in coins})
|
|
367
|
+
)
|
|
368
|
+
targets = {coin: None for coin in coins}
|
|
362
369
|
for position in positions:
|
|
363
|
-
|
|
364
|
-
|
|
370
|
+
coin = position["coin"]
|
|
371
|
+
if coin in targets:
|
|
372
|
+
targets[coin] = position
|
|
365
373
|
|
|
366
|
-
|
|
374
|
+
orders = []
|
|
375
|
+
for coin in coins:
|
|
376
|
+
target = targets[coin]
|
|
377
|
+
if target is None:
|
|
378
|
+
continue
|
|
379
|
+
size = float(target["szi"])
|
|
380
|
+
orders.append(
|
|
381
|
+
{"coin": coin, "is_buy": size < 0, "sz": abs(size), "px": 0, "ro": True}
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
if not orders:
|
|
367
385
|
return None
|
|
368
386
|
|
|
369
|
-
|
|
370
|
-
price = await self.get_mid_price(coin)
|
|
371
|
-
if not price:
|
|
372
|
-
raise ValueError(f"Failed to retrieve market price for {coin}")
|
|
373
|
-
|
|
374
|
-
close_order = {
|
|
375
|
-
"coin": coin,
|
|
376
|
-
"is_buy": size < 0,
|
|
377
|
-
"sz": abs(size),
|
|
378
|
-
"px": price,
|
|
379
|
-
"is_market": True,
|
|
380
|
-
"ro": True,
|
|
381
|
-
}
|
|
387
|
+
return await self.batch_place_orders(orders, is_market=True)
|
|
382
388
|
|
|
383
|
-
|
|
389
|
+
async def close_position(self, coin: str):
|
|
390
|
+
return await self.close_positions([coin])
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from types import TracebackType
|
|
3
|
+
from typing import Any
|
|
4
|
+
from traceback import TracebackException
|
|
5
|
+
|
|
6
|
+
from aiohttp import ClientSession
|
|
7
|
+
|
|
8
|
+
from async_hyperliquid.utils.types import Endpoint
|
|
9
|
+
from async_hyperliquid.utils.constants import MAINNET_API_URL
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
_REDACTED = "<redacted>"
|
|
13
|
+
_SENSITIVE_PAYLOAD_KEYS = frozenset({"signature", "signatures"})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _redact_payload(payload: Any) -> Any:
|
|
17
|
+
if isinstance(payload, dict):
|
|
18
|
+
redacted: dict[str, Any] = {}
|
|
19
|
+
for key, value in payload.items():
|
|
20
|
+
if key in _SENSITIVE_PAYLOAD_KEYS:
|
|
21
|
+
redacted[key] = _REDACTED
|
|
22
|
+
elif key == "action" and isinstance(value, dict):
|
|
23
|
+
redacted[key] = {
|
|
24
|
+
"type": value.get("type"),
|
|
25
|
+
"keys": sorted(value.keys()),
|
|
26
|
+
}
|
|
27
|
+
else:
|
|
28
|
+
redacted[key] = _redact_payload(value)
|
|
29
|
+
return redacted
|
|
30
|
+
|
|
31
|
+
if isinstance(payload, list):
|
|
32
|
+
return [_redact_payload(item) for item in payload]
|
|
33
|
+
|
|
34
|
+
return payload
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AsyncAPI:
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
endpoint: Endpoint,
|
|
41
|
+
base_url: str | None = None,
|
|
42
|
+
session: ClientSession = None, # type: ignore
|
|
43
|
+
*,
|
|
44
|
+
owns_session: bool = True,
|
|
45
|
+
):
|
|
46
|
+
self.endpoint = endpoint
|
|
47
|
+
self.base_url = (base_url or MAINNET_API_URL).rstrip("/")
|
|
48
|
+
self.session = session
|
|
49
|
+
self._owns_session = owns_session
|
|
50
|
+
self._request_url = f"{self.base_url}/{self.endpoint.value}"
|
|
51
|
+
|
|
52
|
+
# for async with AsyncAPI() as api usage
|
|
53
|
+
async def __aenter__(self) -> "AsyncAPI":
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
async def __aexit__(
|
|
57
|
+
self, exc_type: Exception, exc_val: TracebackException, traceback: TracebackType
|
|
58
|
+
) -> None:
|
|
59
|
+
await self.close()
|
|
60
|
+
|
|
61
|
+
async def close(self) -> None:
|
|
62
|
+
if (
|
|
63
|
+
getattr(self, "_owns_session", True)
|
|
64
|
+
and self.session
|
|
65
|
+
and not self.session.closed
|
|
66
|
+
):
|
|
67
|
+
await self.session.close()
|
|
68
|
+
|
|
69
|
+
async def post(self, payload: dict | None = None) -> Any:
|
|
70
|
+
if self.session is None:
|
|
71
|
+
raise RuntimeError("ClientSession is not initialized")
|
|
72
|
+
|
|
73
|
+
payload = payload or {}
|
|
74
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
75
|
+
logger.debug("POST %s %s", self._request_url, _redact_payload(payload))
|
|
76
|
+
|
|
77
|
+
async with self.session.post(self._request_url, json=payload) as resp:
|
|
78
|
+
resp.raise_for_status()
|
|
79
|
+
try:
|
|
80
|
+
return await resp.json()
|
|
81
|
+
except Exception as e:
|
|
82
|
+
logger.error(
|
|
83
|
+
"Error parsing JSON response from %s: %s", self._request_url, e
|
|
84
|
+
)
|
|
85
|
+
return await resp.text()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from collections.abc import Callable
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
4
5
|
from aiohttp import ClientSession
|
|
@@ -8,10 +9,7 @@ from async_hyperliquid.async_api import AsyncAPI
|
|
|
8
9
|
from async_hyperliquid.utils.miscs import get_timestamp_ms
|
|
9
10
|
from async_hyperliquid.utils.types import Endpoint, SignType
|
|
10
11
|
from async_hyperliquid.utils.signing import sign_action, sign_multi_sig_action
|
|
11
|
-
from async_hyperliquid.utils.constants import
|
|
12
|
-
MAINNET_API_URL,
|
|
13
|
-
SIGNATURE_CHAIN_ID,
|
|
14
|
-
)
|
|
12
|
+
from async_hyperliquid.utils.constants import MAINNET_API_URL, SIGNATURE_CHAIN_ID
|
|
15
13
|
|
|
16
14
|
logger = logging.getLogger(__name__)
|
|
17
15
|
|
|
@@ -23,11 +21,13 @@ class ExchangeAPI(AsyncAPI):
|
|
|
23
21
|
session: ClientSession,
|
|
24
22
|
base_url: str | None = None,
|
|
25
23
|
address: str | None = None,
|
|
24
|
+
nonce_factory: Callable[[], int] | None = None,
|
|
26
25
|
):
|
|
27
26
|
self.account = account
|
|
28
27
|
self.address = address or account.address
|
|
29
28
|
self.is_mainnet = base_url == MAINNET_API_URL
|
|
30
|
-
|
|
29
|
+
self._next_nonce = nonce_factory or get_timestamp_ms
|
|
30
|
+
super().__init__(Endpoint.EXCHANGE, base_url, session, owns_session=False)
|
|
31
31
|
|
|
32
32
|
async def multi_sig(
|
|
33
33
|
self,
|
|
@@ -65,7 +65,7 @@ class ExchangeAPI(AsyncAPI):
|
|
|
65
65
|
assert self.endpoint == Endpoint.EXCHANGE, (
|
|
66
66
|
"only exchange endpoint supports action"
|
|
67
67
|
)
|
|
68
|
-
nonce =
|
|
68
|
+
nonce = self._next_nonce()
|
|
69
69
|
# TODO: support multi sig
|
|
70
70
|
signature = sign_action(
|
|
71
71
|
self.account, action, vault, nonce, self.is_mainnet, expires
|
|
@@ -88,5 +88,12 @@ class ExchangeAPI(AsyncAPI):
|
|
|
88
88
|
payload["vaultAddress"] = vault
|
|
89
89
|
if expires:
|
|
90
90
|
payload["expiresAfter"] = expires
|
|
91
|
-
logger.
|
|
91
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
92
|
+
logger.debug(
|
|
93
|
+
"Post action type=%s nonce=%s vault=%s expires=%s",
|
|
94
|
+
action.get("type"),
|
|
95
|
+
nonce,
|
|
96
|
+
bool(vault),
|
|
97
|
+
expires,
|
|
98
|
+
)
|
|
92
99
|
return await self.post(payload)
|
|
@@ -45,7 +45,7 @@ from async_hyperliquid.utils.types import (
|
|
|
45
45
|
|
|
46
46
|
class InfoAPI(AsyncAPI):
|
|
47
47
|
def __init__(self, base_url: str, session: ClientSession):
|
|
48
|
-
super().__init__(Endpoint.INFO, base_url, session)
|
|
48
|
+
super().__init__(Endpoint.INFO, base_url, session, owns_session=False)
|
|
49
49
|
|
|
50
50
|
async def get_all_mids(self, dex: str = "") -> dict[str, int]:
|
|
51
51
|
payload = {"type": "allMids", "dex": dex}
|
|
@@ -86,12 +86,7 @@ class InfoAPI(AsyncAPI):
|
|
|
86
86
|
async def get_order_status(
|
|
87
87
|
self, order_id: str | int, address: str, dex: str = ""
|
|
88
88
|
) -> OrderWithStatus:
|
|
89
|
-
payload = {
|
|
90
|
-
"type": "orderStatus",
|
|
91
|
-
"user": address,
|
|
92
|
-
"oid": order_id,
|
|
93
|
-
"dex": dex,
|
|
94
|
-
}
|
|
89
|
+
payload = {"type": "orderStatus", "user": address, "oid": order_id, "dex": dex}
|
|
95
90
|
return await self.post(payload)
|
|
96
91
|
|
|
97
92
|
async def get_depth(
|
|
@@ -132,9 +127,7 @@ class InfoAPI(AsyncAPI):
|
|
|
132
127
|
payload = {"type": "subAccounts", "user": address}
|
|
133
128
|
return await self.post(payload)
|
|
134
129
|
|
|
135
|
-
async def get_vault_info(
|
|
136
|
-
self, address: str, user: str | None = None
|
|
137
|
-
) -> VaultInfo:
|
|
130
|
+
async def get_vault_info(self, address: str, user: str | None = None) -> VaultInfo:
|
|
138
131
|
payload = {"type": "vaultDetails", "vaultAddress": address}
|
|
139
132
|
if user:
|
|
140
133
|
payload["user"] = user
|
|
@@ -219,9 +212,7 @@ class InfoAPI(AsyncAPI):
|
|
|
219
212
|
is_funding: bool = True,
|
|
220
213
|
) -> UserFundings:
|
|
221
214
|
payload = {
|
|
222
|
-
"type": "userFunding"
|
|
223
|
-
if is_funding
|
|
224
|
-
else "userNonFundingLedgerUpdates",
|
|
215
|
+
"type": "userFunding" if is_funding else "userNonFundingLedgerUpdates",
|
|
225
216
|
"user": address,
|
|
226
217
|
"startTime": start_time,
|
|
227
218
|
"endTime": end_time,
|
|
@@ -266,9 +257,7 @@ class InfoAPI(AsyncAPI):
|
|
|
266
257
|
payload = {"type": "spotMetaAndAssetCtxs"}
|
|
267
258
|
return await self.post(payload)
|
|
268
259
|
|
|
269
|
-
async def get_user_token_balances(
|
|
270
|
-
self, address: str
|
|
271
|
-
) -> SpotClearinghouseState:
|
|
260
|
+
async def get_user_token_balances(self, address: str) -> SpotClearinghouseState:
|
|
272
261
|
return await self.get_spot_clearinghouse_state(address)
|
|
273
262
|
|
|
274
263
|
async def get_spot_clearinghouse_state(
|
|
@@ -37,6 +37,37 @@ from async_hyperliquid.utils.constants import (
|
|
|
37
37
|
CONVERT_TO_MULTI_SIG_USER_SIGN_TYPES,
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
+
_EIP712_DOMAIN_FIELDS = [
|
|
41
|
+
{"name": "name", "type": "string"},
|
|
42
|
+
{"name": "version", "type": "string"},
|
|
43
|
+
{"name": "chainId", "type": "uint256"},
|
|
44
|
+
{"name": "verifyingContract", "type": "address"},
|
|
45
|
+
]
|
|
46
|
+
_ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
|
47
|
+
_EXCHANGE_AGENT_DOMAIN = {
|
|
48
|
+
"chainId": 1337,
|
|
49
|
+
"name": "Exchange",
|
|
50
|
+
"verifyingContract": _ZERO_ADDRESS,
|
|
51
|
+
"version": "1",
|
|
52
|
+
}
|
|
53
|
+
_EXCHANGE_AGENT_MESSAGE_TYPES = {
|
|
54
|
+
"Agent": [
|
|
55
|
+
{"name": "source", "type": "string"},
|
|
56
|
+
{"name": "connectionId", "type": "bytes32"},
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
_EXCHANGE_AGENT_PAYLOAD_BASE = {
|
|
60
|
+
"domain": _EXCHANGE_AGENT_DOMAIN,
|
|
61
|
+
"types": {
|
|
62
|
+
"Agent": _EXCHANGE_AGENT_MESSAGE_TYPES["Agent"],
|
|
63
|
+
"EIP712Domain": _EIP712_DOMAIN_FIELDS,
|
|
64
|
+
},
|
|
65
|
+
"primaryType": "Agent",
|
|
66
|
+
}
|
|
67
|
+
_USER_SIGNED_PAYLOAD_BASE_CACHE: dict[tuple[str, int, int], dict[str, Any]] = {}
|
|
68
|
+
_USER_SIGNED_DOMAIN_CACHE: dict[int, dict[str, Any]] = {}
|
|
69
|
+
_USER_SIGNED_MESSAGE_TYPES_CACHE: dict[tuple[str, int], dict[str, List[dict]]] = {}
|
|
70
|
+
|
|
40
71
|
|
|
41
72
|
def address_to_bytes(address: str) -> bytes:
|
|
42
73
|
return bytes.fromhex(address[2:] if address.startswith("0x") else address)
|
|
@@ -64,11 +95,67 @@ def hash_action(
|
|
|
64
95
|
def sign_inner(wallet: LocalAccount, data: dict) -> SignedAction:
|
|
65
96
|
encodes = encode_typed_data(full_message=data)
|
|
66
97
|
signed = wallet.sign_message(encodes)
|
|
67
|
-
return {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
98
|
+
return {"r": to_hex(signed["r"]), "s": to_hex(signed["s"]), "v": signed["v"]}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def sign_typed_data(
|
|
102
|
+
wallet: LocalAccount,
|
|
103
|
+
domain_data: dict[str, Any],
|
|
104
|
+
message_types: dict[str, Any],
|
|
105
|
+
message_data: dict[str, Any],
|
|
106
|
+
) -> SignedAction:
|
|
107
|
+
encodes = encode_typed_data(
|
|
108
|
+
domain_data=domain_data, message_types=message_types, message_data=message_data
|
|
109
|
+
)
|
|
110
|
+
signed = wallet.sign_message(encodes)
|
|
111
|
+
return {"r": to_hex(signed["r"]), "s": to_hex(signed["s"]), "v": signed["v"]}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _user_signed_domain(chain_id: int) -> dict[str, Any]:
|
|
115
|
+
payload = _USER_SIGNED_DOMAIN_CACHE.get(chain_id)
|
|
116
|
+
if payload is None:
|
|
117
|
+
payload = {
|
|
118
|
+
"name": "HyperliquidSignTransaction",
|
|
119
|
+
"version": "1",
|
|
120
|
+
"chainId": chain_id,
|
|
121
|
+
"verifyingContract": _ZERO_ADDRESS,
|
|
122
|
+
}
|
|
123
|
+
_USER_SIGNED_DOMAIN_CACHE[chain_id] = payload
|
|
124
|
+
return payload
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _user_signed_message_types(
|
|
128
|
+
primary_type: str, payload_types: List[dict]
|
|
129
|
+
) -> dict[str, List[dict]]:
|
|
130
|
+
cache_key = (primary_type, id(payload_types))
|
|
131
|
+
payload = _USER_SIGNED_MESSAGE_TYPES_CACHE.get(cache_key)
|
|
132
|
+
if payload is None:
|
|
133
|
+
payload = {primary_type: payload_types}
|
|
134
|
+
_USER_SIGNED_MESSAGE_TYPES_CACHE[cache_key] = payload
|
|
135
|
+
return payload
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _user_signed_payload_base(
|
|
139
|
+
primary_type: str, payload_types: List[dict], chain_id: int
|
|
140
|
+
):
|
|
141
|
+
cache_key = (primary_type, id(payload_types), chain_id)
|
|
142
|
+
payload = _USER_SIGNED_PAYLOAD_BASE_CACHE.get(cache_key)
|
|
143
|
+
if payload is None:
|
|
144
|
+
payload = {
|
|
145
|
+
"domain": {
|
|
146
|
+
"name": "HyperliquidSignTransaction",
|
|
147
|
+
"version": "1",
|
|
148
|
+
"chainId": chain_id,
|
|
149
|
+
"verifyingContract": _ZERO_ADDRESS,
|
|
150
|
+
},
|
|
151
|
+
"types": {
|
|
152
|
+
primary_type: payload_types,
|
|
153
|
+
"EIP712Domain": _EIP712_DOMAIN_FIELDS,
|
|
154
|
+
},
|
|
155
|
+
"primaryType": primary_type,
|
|
156
|
+
}
|
|
157
|
+
_USER_SIGNED_PAYLOAD_BASE_CACHE[cache_key] = payload
|
|
158
|
+
return payload
|
|
72
159
|
|
|
73
160
|
|
|
74
161
|
def sign_action(
|
|
@@ -81,29 +168,9 @@ def sign_action(
|
|
|
81
168
|
) -> SignedAction:
|
|
82
169
|
h = hash_action(action, active_pool, nonce, expires)
|
|
83
170
|
msg = {"source": "a" if is_mainnet else "b", "connectionId": h}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"name": "Exchange",
|
|
88
|
-
"verifyingContract": "0x0000000000000000000000000000000000000000",
|
|
89
|
-
"version": "1",
|
|
90
|
-
},
|
|
91
|
-
"types": {
|
|
92
|
-
"Agent": [
|
|
93
|
-
{"name": "source", "type": "string"},
|
|
94
|
-
{"name": "connectionId", "type": "bytes32"},
|
|
95
|
-
],
|
|
96
|
-
"EIP712Domain": [
|
|
97
|
-
{"name": "name", "type": "string"},
|
|
98
|
-
{"name": "version", "type": "string"},
|
|
99
|
-
{"name": "chainId", "type": "uint256"},
|
|
100
|
-
{"name": "verifyingContract", "type": "address"},
|
|
101
|
-
],
|
|
102
|
-
},
|
|
103
|
-
"primaryType": "Agent",
|
|
104
|
-
"message": msg,
|
|
105
|
-
}
|
|
106
|
-
return sign_inner(wallet, data)
|
|
171
|
+
return sign_typed_data(
|
|
172
|
+
wallet, _EXCHANGE_AGENT_DOMAIN, _EXCHANGE_AGENT_MESSAGE_TYPES, msg
|
|
173
|
+
)
|
|
107
174
|
|
|
108
175
|
|
|
109
176
|
def round_float(x: float) -> str:
|
|
@@ -124,9 +191,7 @@ def ensure_order_type(order_type: OrderType) -> OrderType:
|
|
|
124
191
|
return {
|
|
125
192
|
"trigger": {
|
|
126
193
|
"isMarket": order_type["trigger"]["isMarket"],
|
|
127
|
-
"triggerPx": round_float(
|
|
128
|
-
float(order_type["trigger"]["triggerPx"])
|
|
129
|
-
),
|
|
194
|
+
"triggerPx": round_float(float(order_type["trigger"]["triggerPx"])),
|
|
130
195
|
"tpsl": order_type["trigger"]["tpsl"],
|
|
131
196
|
}
|
|
132
197
|
}
|
|
@@ -168,22 +233,7 @@ def orders_to_action(
|
|
|
168
233
|
def user_signed_payload(primary_type, payload_types, action):
|
|
169
234
|
chain_id = int(action["signatureChainId"], 16)
|
|
170
235
|
return {
|
|
171
|
-
|
|
172
|
-
"name": "HyperliquidSignTransaction",
|
|
173
|
-
"version": "1",
|
|
174
|
-
"chainId": chain_id,
|
|
175
|
-
"verifyingContract": "0x0000000000000000000000000000000000000000",
|
|
176
|
-
},
|
|
177
|
-
"types": {
|
|
178
|
-
primary_type: payload_types,
|
|
179
|
-
"EIP712Domain": [
|
|
180
|
-
{"name": "name", "type": "string"},
|
|
181
|
-
{"name": "version", "type": "string"},
|
|
182
|
-
{"name": "chainId", "type": "uint256"},
|
|
183
|
-
{"name": "verifyingContract", "type": "address"},
|
|
184
|
-
],
|
|
185
|
-
},
|
|
186
|
-
"primaryType": primary_type,
|
|
236
|
+
**_user_signed_payload_base(primary_type, payload_types, chain_id),
|
|
187
237
|
"message": action,
|
|
188
238
|
}
|
|
189
239
|
|
|
@@ -197,8 +247,13 @@ def sign_user_signed_action(
|
|
|
197
247
|
):
|
|
198
248
|
action["signatureChainId"] = SIGNATURE_CHAIN_ID
|
|
199
249
|
action["hyperliquidChain"] = "Mainnet" if is_mainnet else "Testnet"
|
|
200
|
-
|
|
201
|
-
return
|
|
250
|
+
chain_id = int(action["signatureChainId"], 16)
|
|
251
|
+
return sign_typed_data(
|
|
252
|
+
wallet,
|
|
253
|
+
_user_signed_domain(chain_id),
|
|
254
|
+
_user_signed_message_types(primary_type, payload_types),
|
|
255
|
+
action,
|
|
256
|
+
)
|
|
202
257
|
|
|
203
258
|
|
|
204
259
|
def sign_usd_transfer_action(wallet: LocalAccount, action, is_mainnet: bool):
|
|
@@ -211,9 +266,7 @@ def sign_usd_transfer_action(wallet: LocalAccount, action, is_mainnet: bool):
|
|
|
211
266
|
)
|
|
212
267
|
|
|
213
268
|
|
|
214
|
-
def sign_spot_transfer_action(
|
|
215
|
-
wallet: LocalAccount, action: dict, is_mainnet: bool
|
|
216
|
-
):
|
|
269
|
+
def sign_spot_transfer_action(wallet: LocalAccount, action: dict, is_mainnet: bool):
|
|
217
270
|
return sign_user_signed_action(
|
|
218
271
|
wallet,
|
|
219
272
|
action,
|
|
@@ -233,9 +286,7 @@ def sign_withdraw_action(wallet: LocalAccount, action: dict, is_mainnet: bool):
|
|
|
233
286
|
)
|
|
234
287
|
|
|
235
288
|
|
|
236
|
-
def sign_usd_class_transfer_action(
|
|
237
|
-
wallet: LocalAccount, action: Any, is_mainnet: bool
|
|
238
|
-
):
|
|
289
|
+
def sign_usd_class_transfer_action(wallet: LocalAccount, action: Any, is_mainnet: bool):
|
|
239
290
|
return sign_user_signed_action(
|
|
240
291
|
wallet,
|
|
241
292
|
action,
|
|
@@ -245,9 +296,7 @@ def sign_usd_class_transfer_action(
|
|
|
245
296
|
)
|
|
246
297
|
|
|
247
298
|
|
|
248
|
-
def sign_send_asset_action(
|
|
249
|
-
wallet: LocalAccount, action: dict, is_mainnet: bool
|
|
250
|
-
):
|
|
299
|
+
def sign_send_asset_action(wallet: LocalAccount, action: dict, is_mainnet: bool):
|
|
251
300
|
return sign_user_signed_action(
|
|
252
301
|
wallet,
|
|
253
302
|
action,
|
|
@@ -257,9 +306,7 @@ def sign_send_asset_action(
|
|
|
257
306
|
)
|
|
258
307
|
|
|
259
308
|
|
|
260
|
-
def sign_staking_deposit_action(
|
|
261
|
-
wallet: LocalAccount, action: dict, is_mainnet: bool
|
|
262
|
-
):
|
|
309
|
+
def sign_staking_deposit_action(wallet: LocalAccount, action: dict, is_mainnet: bool):
|
|
263
310
|
return sign_user_signed_action(
|
|
264
311
|
wallet,
|
|
265
312
|
action,
|
|
@@ -269,9 +316,7 @@ def sign_staking_deposit_action(
|
|
|
269
316
|
)
|
|
270
317
|
|
|
271
318
|
|
|
272
|
-
def sign_staking_withdraw_action(
|
|
273
|
-
wallet: LocalAccount, action: dict, is_mainnet: bool
|
|
274
|
-
):
|
|
319
|
+
def sign_staking_withdraw_action(wallet: LocalAccount, action: dict, is_mainnet: bool):
|
|
275
320
|
return sign_user_signed_action(
|
|
276
321
|
wallet,
|
|
277
322
|
action,
|
|
@@ -281,9 +326,7 @@ def sign_staking_withdraw_action(
|
|
|
281
326
|
)
|
|
282
327
|
|
|
283
328
|
|
|
284
|
-
def sign_token_delegate_action(
|
|
285
|
-
wallet: LocalAccount, action: dict, is_mainnet: bool
|
|
286
|
-
):
|
|
329
|
+
def sign_token_delegate_action(wallet: LocalAccount, action: dict, is_mainnet: bool):
|
|
287
330
|
return sign_user_signed_action(
|
|
288
331
|
wallet,
|
|
289
332
|
action,
|
|
@@ -293,9 +336,7 @@ def sign_token_delegate_action(
|
|
|
293
336
|
)
|
|
294
337
|
|
|
295
338
|
|
|
296
|
-
def sign_approve_agent_action(
|
|
297
|
-
wallet: LocalAccount, action: dict, is_mainnet: bool
|
|
298
|
-
):
|
|
339
|
+
def sign_approve_agent_action(wallet: LocalAccount, action: dict, is_mainnet: bool):
|
|
299
340
|
return sign_user_signed_action(
|
|
300
341
|
wallet,
|
|
301
342
|
action,
|
|
@@ -690,11 +690,7 @@ class UserVaultWithdraw(TypedDict):
|
|
|
690
690
|
|
|
691
691
|
|
|
692
692
|
UserNonFundingDelta = (
|
|
693
|
-
UserDeposit
|
|
694
|
-
| UserWithdraw
|
|
695
|
-
| UserTransfer
|
|
696
|
-
| UserVaultDeposit
|
|
697
|
-
| UserVaultWithdraw
|
|
693
|
+
UserDeposit | UserWithdraw | UserTransfer | UserVaultDeposit | UserVaultWithdraw
|
|
698
694
|
)
|
|
699
695
|
|
|
700
696
|
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from types import TracebackType
|
|
3
|
-
from typing import Any
|
|
4
|
-
from traceback import TracebackException
|
|
5
|
-
|
|
6
|
-
from aiohttp import ClientSession
|
|
7
|
-
|
|
8
|
-
from async_hyperliquid.utils.types import Endpoint
|
|
9
|
-
from async_hyperliquid.utils.constants import MAINNET_API_URL
|
|
10
|
-
|
|
11
|
-
logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class AsyncAPI:
|
|
15
|
-
def __init__(
|
|
16
|
-
self,
|
|
17
|
-
endpoint: Endpoint,
|
|
18
|
-
base_url: str | None = None,
|
|
19
|
-
session: ClientSession = None, # type: ignore
|
|
20
|
-
):
|
|
21
|
-
self.endpoint = endpoint
|
|
22
|
-
self.base_url = base_url or MAINNET_API_URL
|
|
23
|
-
self.session = session
|
|
24
|
-
|
|
25
|
-
# for async with AsyncAPI() as api usage
|
|
26
|
-
async def __aenter__(self) -> "AsyncAPI":
|
|
27
|
-
return self
|
|
28
|
-
|
|
29
|
-
async def __aexit__(
|
|
30
|
-
self,
|
|
31
|
-
exc_type: Exception,
|
|
32
|
-
exc_val: TracebackException,
|
|
33
|
-
traceback: TracebackType,
|
|
34
|
-
) -> None:
|
|
35
|
-
await self.close()
|
|
36
|
-
|
|
37
|
-
async def close(self) -> None:
|
|
38
|
-
if self.session and not self.session.closed:
|
|
39
|
-
await self.session.close()
|
|
40
|
-
|
|
41
|
-
async def post(self, payload: dict | None = None) -> Any:
|
|
42
|
-
payload = payload or {}
|
|
43
|
-
req_path = f"{self.base_url}/{self.endpoint.value}"
|
|
44
|
-
logger.debug(f"POST {req_path} {payload}")
|
|
45
|
-
async with self.session.post(req_path, json=payload) as resp:
|
|
46
|
-
resp.raise_for_status()
|
|
47
|
-
try:
|
|
48
|
-
return await resp.json()
|
|
49
|
-
except Exception as e:
|
|
50
|
-
logger.error(f"Error parsing JSON response: {e}")
|
|
51
|
-
return await resp.text()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/async_hyperliquid.py
RENAMED
|
File without changes
|
|
File without changes
|
{async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/constants.py
RENAMED
|
File without changes
|
{async_hyperliquid-0.4.3 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/decorators.py
RENAMED
|
File without changes
|
|
File without changes
|