async-hyperliquid 0.4.3__tar.gz → 0.4.5__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.5}/PKG-INFO +2 -1
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/pyproject.toml +2 -1
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/_async_hyperliquid/actions.py +23 -45
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/_async_hyperliquid/core.py +83 -39
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/_async_hyperliquid/info.py +60 -32
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/_async_hyperliquid/orders.py +71 -64
- async_hyperliquid-0.4.5/src/async_hyperliquid/async_api.py +85 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/exchange.py +14 -7
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/info.py +7 -18
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/utils/signing.py +111 -70
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/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.5}/LICENSE +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/README.md +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/__init__.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/_async_hyperliquid/__init__.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/async_hyperliquid.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/utils/__init__.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/utils/constants.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/utils/decorators.py +0 -0
- {async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/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.5
|
|
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.5"
|
|
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.5}/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 = {
|
|
@@ -136,6 +164,13 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
136
164
|
}
|
|
137
165
|
|
|
138
166
|
async def init_metas(self) -> None:
|
|
167
|
+
# TODO: Add HIP-4 outcome meta initialization from the spot info
|
|
168
|
+
# `outcomeMeta` endpoint once outcomes move beyond testnet-only rollout.
|
|
169
|
+
# Outcome asset IDs do not follow the current perp/spot offset scheme:
|
|
170
|
+
# `encoding = 10 * outcome + side`, coin names use `#{encoding}`, token
|
|
171
|
+
# names use `+{encoding}`, and `asset_id = 100_000_000 + encoding`, so
|
|
172
|
+
# this path will need dedicated outcome mappings instead of reusing the
|
|
173
|
+
# existing perp/spot logic.
|
|
139
174
|
meta_task = self.info.get_perp_meta()
|
|
140
175
|
spot_meta_task = self.info.get_spot_meta()
|
|
141
176
|
all_dex_names_task = self.get_all_dex_name()
|
|
@@ -171,24 +206,31 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
171
206
|
|
|
172
207
|
async def get_metas(self, perp_only: bool = False) -> Metas:
|
|
173
208
|
metas: Metas = {"perp": {}, "spot": [], "dexs": {}} # type: ignore
|
|
174
|
-
perp_meta = await self.info.get_perp_meta()
|
|
175
209
|
if perp_only:
|
|
176
|
-
metas["perp"] =
|
|
210
|
+
metas["perp"] = await self.info.get_perp_meta()
|
|
177
211
|
return metas
|
|
178
212
|
|
|
179
|
-
|
|
213
|
+
perp_meta, spot_meta = await asyncio.gather(
|
|
214
|
+
self.info.get_perp_meta(), self.info.get_spot_meta()
|
|
215
|
+
)
|
|
216
|
+
metas["perp"] = perp_meta
|
|
217
|
+
metas["spot"] = spot_meta
|
|
180
218
|
return metas
|
|
181
219
|
|
|
182
220
|
async def get_all_metas(self) -> Metas:
|
|
183
|
-
dexs = await
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
221
|
+
dexs, perp_meta, spot_meta = await asyncio.gather(
|
|
222
|
+
self.get_all_dex_name(),
|
|
223
|
+
self.info.get_perp_meta(),
|
|
224
|
+
self.info.get_spot_meta(),
|
|
225
|
+
)
|
|
226
|
+
dex_metas: dict[str, PerpMeta] = {}
|
|
227
|
+
if len(dexs) > 1:
|
|
228
|
+
dex_meta_results = await asyncio.gather(
|
|
229
|
+
*(self.info.get_perp_meta(dex) for dex in dexs[1:])
|
|
230
|
+
)
|
|
231
|
+
dex_metas = {
|
|
232
|
+
dex: meta for dex, meta in zip(dexs[1:], dex_meta_results, strict=True)
|
|
233
|
+
}
|
|
192
234
|
return {"perp": perp_meta, "spot": spot_meta, "dexs": dex_metas}
|
|
193
235
|
|
|
194
236
|
async def get_all_dex_name(self) -> list[str]:
|
|
@@ -212,7 +254,6 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
212
254
|
|
|
213
255
|
async def get_coin_asset(self, coin: str) -> int:
|
|
214
256
|
coin_name = await self.get_coin_name(coin)
|
|
215
|
-
|
|
216
257
|
if coin_name not in self.coin_assets:
|
|
217
258
|
raise ValueError(f"Coin {coin}({coin_name}) not found")
|
|
218
259
|
|
|
@@ -224,7 +265,10 @@ class AsyncHyperliquidCore(AsyncAPI):
|
|
|
224
265
|
|
|
225
266
|
async def get_coin_sz_decimals(self, coin: str) -> int:
|
|
226
267
|
coin_name = await self.get_coin_name(coin)
|
|
227
|
-
|
|
268
|
+
if coin_name not in self.coin_assets:
|
|
269
|
+
raise ValueError(f"Coin {coin}({coin_name}) not found")
|
|
270
|
+
|
|
271
|
+
asset = self.coin_assets[coin_name]
|
|
228
272
|
return self.asset_sz_decimals[asset]
|
|
229
273
|
|
|
230
274
|
async def get_token_info(self, coin: str) -> SpotTokenMeta:
|
{async_hyperliquid-0.4.3 → async_hyperliquid-0.4.5}/src/async_hyperliquid/_async_hyperliquid/info.py
RENAMED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import warnings
|
|
3
|
-
from typing import Literal
|
|
3
|
+
from typing import Literal, cast
|
|
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,
|
|
13
9
|
AccountState,
|
|
14
10
|
ClearinghouseState,
|
|
15
11
|
OrderWithStatus,
|
|
12
|
+
PerpMeta,
|
|
13
|
+
PerpMetaCtxItem,
|
|
16
14
|
Portfolio,
|
|
17
15
|
Position,
|
|
16
|
+
SpotMeta,
|
|
18
17
|
SpotClearinghouseState,
|
|
18
|
+
SpotMetaCtxItem,
|
|
19
19
|
UserDeposit,
|
|
20
20
|
UserNonFundingDelta,
|
|
21
21
|
UserOpenOrders,
|
|
@@ -27,13 +27,49 @@ from .core import AsyncHyperliquidCore
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
30
|
+
async def _get_perp_mark_price(self, coin_name: str, dex: str = "") -> float:
|
|
31
|
+
meta_ctx = await self.info.get_perp_meta_ctx(dex)
|
|
32
|
+
meta = cast(PerpMeta, meta_ctx[0])
|
|
33
|
+
asset_ctxs = cast(list[PerpMetaCtxItem], meta_ctx[1])
|
|
34
|
+
|
|
35
|
+
for idx, info in enumerate(meta["universe"]):
|
|
36
|
+
if info["name"] == coin_name:
|
|
37
|
+
return float(asset_ctxs[idx]["markPx"])
|
|
38
|
+
|
|
39
|
+
raise ValueError(f"Coin {coin_name} not found in perp dex '{dex}'")
|
|
40
|
+
|
|
41
|
+
async def _get_spot_mark_price(self, coin_name: str) -> float:
|
|
42
|
+
meta_ctx = await self.info.get_spot_meta_ctx()
|
|
43
|
+
meta = cast(SpotMeta, meta_ctx[0])
|
|
44
|
+
asset_ctxs = cast(list[SpotMetaCtxItem], meta_ctx[1])
|
|
45
|
+
|
|
46
|
+
for info in meta["universe"]:
|
|
47
|
+
if info["name"] == coin_name:
|
|
48
|
+
asset_index = info["index"]
|
|
49
|
+
return float(asset_ctxs[asset_index]["markPx"])
|
|
50
|
+
|
|
51
|
+
raise ValueError(f"Coin {coin_name} not found in spot metadata")
|
|
52
|
+
|
|
53
|
+
async def get_mark_price(self, coin: str) -> float:
|
|
54
|
+
if ":" in coin:
|
|
55
|
+
return await self._get_perp_mark_price(coin, get_coin_dex(coin))
|
|
56
|
+
|
|
57
|
+
coin_name = await self.get_coin_name(coin)
|
|
58
|
+
if coin_name not in self.coin_assets:
|
|
59
|
+
raise ValueError(f"Coin {coin}({coin_name}) not found")
|
|
60
|
+
|
|
61
|
+
asset = self.coin_assets[coin_name]
|
|
62
|
+
is_spot_asset = SPOT_OFFSET <= asset < PERP_DEX_OFFSET
|
|
63
|
+
if is_spot_asset:
|
|
64
|
+
return await self._get_spot_mark_price(coin_name)
|
|
65
|
+
|
|
66
|
+
return await self._get_perp_mark_price(coin_name, get_coin_dex(coin_name))
|
|
67
|
+
|
|
30
68
|
async def get_market_price(self, coin: str) -> float:
|
|
31
69
|
warnings.warn(
|
|
32
70
|
"get_market_price is deprecated and will remove in the future, use get_mid_price instead"
|
|
33
71
|
)
|
|
34
|
-
|
|
35
|
-
market_prices = await self.get_all_market_prices()
|
|
36
|
-
return market_prices[coin_name]
|
|
72
|
+
return await self.get_mark_price(coin)
|
|
37
73
|
|
|
38
74
|
async def get_all_market_prices(
|
|
39
75
|
self, market: Literal["spot", "perp", "all"] = "all"
|
|
@@ -50,10 +86,15 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
50
86
|
await self.init_metas()
|
|
51
87
|
spot_data = None
|
|
52
88
|
perp_data = None
|
|
53
|
-
if
|
|
54
|
-
spot_data = await
|
|
55
|
-
|
|
56
|
-
|
|
89
|
+
if is_all:
|
|
90
|
+
spot_data, perp_data = await asyncio.gather(
|
|
91
|
+
self.info.get_spot_meta_ctx(), self.info.get_perp_meta_ctx()
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
if is_spot:
|
|
95
|
+
spot_data = await self.info.get_spot_meta_ctx()
|
|
96
|
+
if is_perp:
|
|
97
|
+
perp_data = await self.info.get_perp_meta_ctx()
|
|
57
98
|
|
|
58
99
|
is_perp = is_perp or is_all
|
|
59
100
|
is_spot = is_spot or is_all
|
|
@@ -102,9 +143,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
102
143
|
address = self.address
|
|
103
144
|
return await self.info.get_spot_clearinghouse_state(address)
|
|
104
145
|
|
|
105
|
-
async def get_account_state(
|
|
106
|
-
self, address: str | None = None
|
|
107
|
-
) -> AccountState:
|
|
146
|
+
async def get_account_state(self, address: str | None = None) -> AccountState:
|
|
108
147
|
if not address:
|
|
109
148
|
address = self.address
|
|
110
149
|
|
|
@@ -127,9 +166,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
127
166
|
|
|
128
167
|
return account_state
|
|
129
168
|
|
|
130
|
-
async def get_account_portfolio(
|
|
131
|
-
self, address: str | None = None
|
|
132
|
-
) -> Portfolio:
|
|
169
|
+
async def get_account_portfolio(self, address: str | None = None) -> Portfolio:
|
|
133
170
|
if not address:
|
|
134
171
|
address = self.address
|
|
135
172
|
|
|
@@ -158,9 +195,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
158
195
|
start_time: int | None = None,
|
|
159
196
|
end_time: int | None = None,
|
|
160
197
|
) -> list[UserDeposit]:
|
|
161
|
-
return await self.get_latest_ledgers(
|
|
162
|
-
"deposit", address, start_time, end_time
|
|
163
|
-
) # type: ignore
|
|
198
|
+
return await self.get_latest_ledgers("deposit", address, start_time, end_time) # type: ignore
|
|
164
199
|
|
|
165
200
|
async def get_latest_withdraws(
|
|
166
201
|
self,
|
|
@@ -168,9 +203,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
168
203
|
start_time: int | None = None,
|
|
169
204
|
end_time: int | None = None,
|
|
170
205
|
) -> list[UserWithdraw]:
|
|
171
|
-
return await self.get_latest_ledgers(
|
|
172
|
-
"withdraw", address, start_time, end_time
|
|
173
|
-
) # type: ignore
|
|
206
|
+
return await self.get_latest_ledgers("withdraw", address, start_time, end_time) # type: ignore
|
|
174
207
|
|
|
175
208
|
async def get_latest_transfers(
|
|
176
209
|
self,
|
|
@@ -183,10 +216,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
183
216
|
) # type: ignore
|
|
184
217
|
|
|
185
218
|
async def get_user_open_orders(
|
|
186
|
-
self,
|
|
187
|
-
address: str | None = None,
|
|
188
|
-
is_frontend: bool = False,
|
|
189
|
-
dex: str = "",
|
|
219
|
+
self, address: str | None = None, is_frontend: bool = False, dex: str = ""
|
|
190
220
|
) -> UserOpenOrders:
|
|
191
221
|
if not address:
|
|
192
222
|
address = self.address
|
|
@@ -226,9 +256,7 @@ class AsyncHyperliquidInfoClient(AsyncHyperliquidCore):
|
|
|
226
256
|
positions.extend(result)
|
|
227
257
|
return positions
|
|
228
258
|
|
|
229
|
-
async def get_user_abstraction(
|
|
230
|
-
self, address: str | None = None
|
|
231
|
-
) -> Abstraction:
|
|
259
|
+
async def get_user_abstraction(self, address: str | None = None) -> Abstraction:
|
|
232
260
|
if not address:
|
|
233
261
|
address = self.address
|
|
234
262
|
return await self.info.get_user_abstraction(address)
|