async-hyperliquid 0.4.2__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.
Files changed (22) hide show
  1. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/PKG-INFO +2 -1
  2. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/pyproject.toml +3 -2
  3. async_hyperliquid-0.4.4/src/async_hyperliquid/_async_hyperliquid/__init__.py +3 -0
  4. async_hyperliquid-0.4.4/src/async_hyperliquid/_async_hyperliquid/actions.py +261 -0
  5. async_hyperliquid-0.4.4/src/async_hyperliquid/_async_hyperliquid/core.py +276 -0
  6. async_hyperliquid-0.4.4/src/async_hyperliquid/_async_hyperliquid/info.py +222 -0
  7. async_hyperliquid-0.4.4/src/async_hyperliquid/_async_hyperliquid/orders.py +390 -0
  8. async_hyperliquid-0.4.4/src/async_hyperliquid/async_api.py +85 -0
  9. async_hyperliquid-0.4.4/src/async_hyperliquid/async_hyperliquid.py +214 -0
  10. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/src/async_hyperliquid/exchange.py +14 -7
  11. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/src/async_hyperliquid/info.py +5 -16
  12. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/signing.py +111 -70
  13. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/types.py +1 -5
  14. async_hyperliquid-0.4.2/src/async_hyperliquid/async_api.py +0 -51
  15. async_hyperliquid-0.4.2/src/async_hyperliquid/async_hyperliquid.py +0 -1138
  16. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/LICENSE +0 -0
  17. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/README.md +0 -0
  18. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/src/async_hyperliquid/__init__.py +0 -0
  19. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/__init__.py +0 -0
  20. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/constants.py +0 -0
  21. {async_hyperliquid-0.4.2 → async_hyperliquid-0.4.4}/src/async_hyperliquid/utils/decorators.py +0 -0
  22. {async_hyperliquid-0.4.2 → 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.2
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.2"
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"
@@ -35,11 +35,12 @@ classifiers = [
35
35
  ]
36
36
  requires-python = ">=3.10,<4"
37
37
  dependencies = [
38
- "aiohttp (>=3.11.12,<4.0.0)",
38
+ "aiohttp>=3.11.12,<4.0.0",
39
39
  "msgpack (>=1.1.0,<2.0.0)",
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]
@@ -0,0 +1,3 @@
1
+ from .actions import AsyncHyperliquidActionsClient
2
+
3
+ __all__ = ["AsyncHyperliquidActionsClient"]
@@ -0,0 +1,261 @@
1
+ import math
2
+ import re
3
+ import warnings
4
+
5
+ from async_hyperliquid.utils.constants import HYPE_FACTOR, MAINNET_API_URL, USD_FACTOR
6
+ from async_hyperliquid.utils.decorators import private_key_required
7
+ from async_hyperliquid.utils.miscs import round_token_amount
8
+ from async_hyperliquid.utils.signing import (
9
+ sign_approve_agent_action,
10
+ sign_approve_builder_fee_action,
11
+ sign_convert_to_multi_sig_user_action,
12
+ sign_send_asset_action,
13
+ sign_spot_transfer_action,
14
+ sign_staking_deposit_action,
15
+ sign_staking_withdraw_action,
16
+ sign_token_delegate_action,
17
+ sign_usd_class_transfer_action,
18
+ sign_usd_transfer_action,
19
+ sign_user_dex_abstraction_action,
20
+ sign_user_set_abstraction_action,
21
+ sign_withdraw_action,
22
+ )
23
+ from async_hyperliquid.utils.types import AgentAbstraction, UserSetAbstraction
24
+
25
+ from .orders import AsyncHyperliquidOrdersClient
26
+
27
+
28
+ class AsyncHyperliquidActionsClient(AsyncHyperliquidOrdersClient):
29
+ async def set_referrer_code(self, code: str):
30
+ action = {"type": "setReferrer", "code": code}
31
+ return await self.exchange.post_action(action)
32
+
33
+ @private_key_required
34
+ async def create_sub_account(self, name: str):
35
+ action = {"type": "createSubAccount", "name": name}
36
+ return await self.exchange.post_action(action)
37
+
38
+ @private_key_required
39
+ async def usd_transfer(self, amount: float, dest: str):
40
+ nonce = self.next_nonce()
41
+ action = {
42
+ "type": "usdSend",
43
+ "amount": round_token_amount(amount, 2),
44
+ "destination": dest,
45
+ "time": nonce,
46
+ }
47
+ is_mainnet = self.base_url == MAINNET_API_URL
48
+ sig = sign_usd_transfer_action(self.account, action, is_mainnet)
49
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
50
+
51
+ @private_key_required
52
+ async def spot_transfer(self, coin: str, amount: float, dest: str):
53
+ token_info = await self.get_token_info(coin)
54
+ token_name = token_info["name"]
55
+ token_id = token_info["tokenId"]
56
+ wei_decimals = token_info["weiDecimals"]
57
+ token = f"{token_name}:{token_id}"
58
+ nonce = self.next_nonce()
59
+ action = {
60
+ "type": "spotSend",
61
+ "destination": dest,
62
+ "token": token,
63
+ "amount": round_token_amount(amount, wei_decimals),
64
+ "time": nonce,
65
+ }
66
+ sig = sign_spot_transfer_action(self.account, action, self.is_mainnet)
67
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
68
+
69
+ @private_key_required
70
+ async def initiate_withdrawal(self, amount: float):
71
+ nonce = self.next_nonce()
72
+ action = {
73
+ "type": "withdraw3",
74
+ "amount": round_token_amount(amount, 2),
75
+ "time": nonce,
76
+ "destination": self.address,
77
+ }
78
+ sig = sign_withdraw_action(self.account, action, self.is_mainnet)
79
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
80
+
81
+ @private_key_required
82
+ async def usd_class_transfer(self, amount: float, to_perp: bool = False):
83
+ nonce = self.next_nonce()
84
+ action = {
85
+ "type": "usdClassTransfer",
86
+ "amount": round_token_amount(amount, 2),
87
+ "toPerp": to_perp,
88
+ "nonce": nonce,
89
+ }
90
+ sig = sign_usd_class_transfer_action(
91
+ self.account, action, self.base_url == MAINNET_API_URL
92
+ )
93
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
94
+
95
+ @private_key_required
96
+ async def send_asset(
97
+ self,
98
+ coin: str,
99
+ amount: float,
100
+ dest: str,
101
+ source_dex: str,
102
+ dest_dex: str,
103
+ sub_account: str = "",
104
+ ):
105
+ token_info = await self.get_token_info(coin)
106
+ token_name = token_info["name"]
107
+ token_id = token_info["tokenId"]
108
+ wei_decimals = token_info["weiDecimals"]
109
+ token = f"{token_name}:{token_id}"
110
+ nonce = self.next_nonce()
111
+ action = {
112
+ "type": "sendAsset",
113
+ "token": token,
114
+ "amount": round_token_amount(amount, wei_decimals),
115
+ "destination": dest,
116
+ "sourceDex": source_dex,
117
+ "destinationDex": dest_dex,
118
+ "fromSubAccount": sub_account,
119
+ "nonce": nonce,
120
+ }
121
+ sig = sign_send_asset_action(self.account, action, self.is_mainnet)
122
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
123
+
124
+ @private_key_required
125
+ async def staking_deposit(self, amount: float):
126
+ amount_in_wei = int(math.floor(amount * HYPE_FACTOR))
127
+ nonce = self.next_nonce()
128
+ action = {"type": "cDeposit", "wei": amount_in_wei, "nonce": nonce}
129
+ sig = sign_staking_deposit_action(self.account, action, self.is_mainnet)
130
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
131
+
132
+ @private_key_required
133
+ async def staking_withdraw(self, amount: float):
134
+ amount_in_wei = int(math.floor(amount * HYPE_FACTOR))
135
+ nonce = self.next_nonce()
136
+ action = {"type": "cWithdraw", "wei": amount_in_wei, "nonce": nonce}
137
+ sig = sign_staking_withdraw_action(self.account, action, self.is_mainnet)
138
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
139
+
140
+ @private_key_required
141
+ async def token_delegate(
142
+ self, validator: str, amount: float, is_undelegate: bool = False
143
+ ):
144
+ amount_in_wei = int(math.floor(amount * HYPE_FACTOR))
145
+ nonce = self.next_nonce()
146
+ action = {
147
+ "type": "tokenDelegate",
148
+ "validator": validator,
149
+ "wei": amount_in_wei,
150
+ "isUndelegate": is_undelegate,
151
+ "nonce": nonce,
152
+ }
153
+ sig = sign_token_delegate_action(self.account, action, self.is_mainnet)
154
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
155
+
156
+ @private_key_required
157
+ async def vault_transfer(self, vault: str, amount: float, is_deposit: bool = True):
158
+ usd_amount = int(math.floor(amount * USD_FACTOR))
159
+ action = {
160
+ "type": "vaultTransfer",
161
+ "vaultAddress": vault,
162
+ "isDeposit": is_deposit,
163
+ "usd": usd_amount,
164
+ }
165
+ return await self.exchange.post_action(action)
166
+
167
+ async def approve_agent(self, agent: str, name: str | None = None):
168
+ nonce = self.next_nonce()
169
+ action = {
170
+ "type": "approveAgent",
171
+ "agentAddress": agent,
172
+ "agentName": name or "",
173
+ "nonce": nonce,
174
+ }
175
+ sig = sign_approve_agent_action(self.account, action, self.is_mainnet)
176
+ if name is None:
177
+ del action["agentName"]
178
+
179
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
180
+
181
+ async def approve_builder_fee(self, max_fee_rate: float, builder: str):
182
+ nonce = self.next_nonce()
183
+ action = {
184
+ "type": "approveBuilderFee",
185
+ "maxFeeRate": f"{max_fee_rate:.3%}",
186
+ "builder": builder,
187
+ "nonce": nonce,
188
+ }
189
+ sig = sign_approve_builder_fee_action(self.account, action, self.is_mainnet)
190
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
191
+
192
+ async def convert_to_multi_sig_user(self, users: list[str], threshold: int):
193
+ nonce = self.next_nonce()
194
+ signers = {"authorizedUsers": sorted(users), "threshold": threshold}
195
+ action = {"type": "convertToMultiSigUser", "signers": signers, "nonce": nonce}
196
+ sig = sign_convert_to_multi_sig_user_action(
197
+ self.account, action, self.is_mainnet
198
+ )
199
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
200
+
201
+ async def reserve_request_weight(self, weight: int):
202
+ action = {"type": "reserveRequestWeight", "weight": weight}
203
+ return await self.exchange.post_action(action, expires=self.expires)
204
+
205
+ async def use_big_block(self, enable: bool):
206
+ action = {"type": "evmUserModify", "usingBigBlocks": enable}
207
+ return await self.exchange.post_action(action)
208
+
209
+ async def user_dex_abstraction(self, user: str | None = None, enabled: bool = True):
210
+ warnings.warn(
211
+ "user_dex_abstraction is deprecated and may be removed in a "
212
+ "future release.",
213
+ DeprecationWarning,
214
+ stacklevel=2,
215
+ )
216
+ nonce = self.next_nonce()
217
+ if user is None:
218
+ user = self.address
219
+ action = {
220
+ "type": "userDexAbstraction",
221
+ "user": user.lower(),
222
+ "enabled": enabled,
223
+ "nonce": nonce,
224
+ }
225
+ sig = sign_user_dex_abstraction_action(self.account, action, self.is_mainnet)
226
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
227
+
228
+ async def user_set_abstraction(
229
+ self, abstraction: UserSetAbstraction, user: str | None = None
230
+ ):
231
+ if user is None:
232
+ user = self.address
233
+ if re.fullmatch(r"0x[a-fA-F0-9]{40}", user) is None:
234
+ raise ValueError(f"user must be a 42-char hex address, got: {user!r}")
235
+ nonce = self.next_nonce()
236
+ action = {
237
+ "type": "userSetAbstraction",
238
+ "user": user.lower(),
239
+ "abstraction": abstraction,
240
+ "nonce": nonce,
241
+ }
242
+ sig = sign_user_set_abstraction_action(self.account, action, self.is_mainnet)
243
+ return await self.exchange.post_action_with_sig(action, sig, nonce)
244
+
245
+ async def agent_enable_dex_abstraction(self):
246
+ warnings.warn(
247
+ "agent_enable_dex_abstraction is deprecated and may be removed "
248
+ "in a future release.",
249
+ DeprecationWarning,
250
+ stacklevel=2,
251
+ )
252
+ action = {"type": "agentEnableDexAbstraction"}
253
+ return await self.exchange.post_action(
254
+ action, vault=self.vault, expires=self.expires
255
+ )
256
+
257
+ async def agent_set_abstraction(self, abstraction: AgentAbstraction):
258
+ action = {"type": "agentSetAbstraction", "abstraction": abstraction}
259
+ return await self.exchange.post_action(
260
+ action, vault=self.vault, expires=self.expires
261
+ )
@@ -0,0 +1,276 @@
1
+ import asyncio
2
+ from threading import Lock
3
+
4
+ from aiohttp import TCPConnector, BaseConnector, ClientSession, ClientTimeout
5
+ from eth_account import Account
6
+ from hl_web3.info import Info as EVMInfo
7
+ from hl_web3.exchange import Exchange as EVMExchange
8
+ from hl_web3.utils.constants import HL_RPC_URL, HL_TESTNET_RPC_URL
9
+ from eth_account.signers.local import LocalAccount
10
+
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
16
+ from async_hyperliquid.utils.constants import (
17
+ SPOT_OFFSET,
18
+ MAINNET_API_URL,
19
+ PERP_DEX_OFFSET,
20
+ TESTNET_API_URL,
21
+ )
22
+
23
+
24
+ class AsyncHyperliquidCore(AsyncAPI):
25
+ address: str
26
+ is_mainnet: bool
27
+ account: LocalAccount
28
+ session: ClientSession
29
+ base_url: str
30
+ vault: str | None
31
+
32
+ coin_assets: dict[str, int]
33
+ coin_names: dict[str, str]
34
+ coin_symbols: dict[str, str]
35
+ asset_sz_decimals: dict[int, int]
36
+ spot_tokens: dict[str, SpotTokenMeta]
37
+
38
+ perp_dexs: list[str]
39
+
40
+ enable_evm: bool
41
+ evm_info: EVMInfo
42
+ evm_exchange: EVMExchange
43
+
44
+ def __init__(
45
+ self,
46
+ address: str,
47
+ api_key: str,
48
+ is_mainnet: bool = True,
49
+ enable_evm: bool = False,
50
+ evm_rpc_url: str | None = None,
51
+ private_key: str | None = None,
52
+ vault: str | None = None,
53
+ perp_dexs: list[str] = [""],
54
+ session: ClientSession | None = None,
55
+ timeout: ClientTimeout | None = None,
56
+ connector: BaseConnector | None = None,
57
+ ):
58
+ self.address = address
59
+ self.is_mainnet = is_mainnet
60
+ self.account = Account.from_key(api_key)
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
+ )
67
+ self.base_url = MAINNET_API_URL if is_mainnet else TESTNET_API_URL
68
+ self.info = InfoAPI(self.base_url, self.session)
69
+ self.exchange = ExchangeAPI(
70
+ self.account,
71
+ self.session,
72
+ self.base_url,
73
+ address=self.address,
74
+ nonce_factory=self.next_nonce,
75
+ )
76
+
77
+ self.coin_assets = {}
78
+ self.coin_names = {}
79
+ self.coin_symbols = {}
80
+ self.asset_sz_decimals = {}
81
+ self.spot_tokens = {}
82
+
83
+ self.vault = vault
84
+ self.expires: int | None = None
85
+ self.perp_dexs = perp_dexs
86
+
87
+ if enable_evm:
88
+ self._init_evm_client(private_key, evm_rpc_url)
89
+
90
+ def set_expires(self, expires: int | None) -> None:
91
+ self.expires = expires
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
+
112
+ def _init_evm_client(
113
+ self, private_key: str | None, rpc_url: str | None = None
114
+ ) -> None:
115
+ if rpc_url is None:
116
+ rpc_url = HL_RPC_URL if self.is_mainnet else HL_TESTNET_RPC_URL
117
+
118
+ self.evm_info = EVMInfo(rpc_url)
119
+
120
+ if private_key is None:
121
+ if self.account.address != self.address:
122
+ raise ValueError("EVM Exchange client can not init without private key")
123
+ private_key = self.account.key.hex()
124
+
125
+ self.evm_exchange = EVMExchange(rpc_url, private_key)
126
+
127
+ def _init_perp_meta(self, meta: PerpMeta, offset: int) -> None:
128
+ for asset, info in enumerate(meta["universe"]):
129
+ asset += offset
130
+ asset_name = info["name"]
131
+ self.coin_assets[asset_name] = asset
132
+ self.coin_names[asset_name] = asset_name
133
+ self.asset_sz_decimals[asset] = info["szDecimals"]
134
+
135
+ def _init_spot_meta(self, meta: SpotMeta) -> None:
136
+ tokens = meta["tokens"]
137
+ total_tokens = len(tokens)
138
+ for info in meta["universe"]:
139
+ asset = info["index"] + SPOT_OFFSET
140
+ asset_name = info["name"]
141
+
142
+ self.coin_assets[asset_name] = asset
143
+ self.coin_names[asset_name] = asset_name
144
+
145
+ base, quote = info["tokens"]
146
+ if not 0 <= base < total_tokens or not 0 <= quote < total_tokens:
147
+ continue
148
+
149
+ base_info = tokens[base]
150
+ base_name = base_info["name"]
151
+ quote_info = tokens[quote]
152
+ quote_name = quote_info["name"]
153
+ name = f"{base_name}/{quote_name}"
154
+ self.coin_names.setdefault(name, asset_name)
155
+ self.coin_names.setdefault(quote_name, quote_name)
156
+
157
+ self.asset_sz_decimals[asset] = base_info["szDecimals"]
158
+ self.spot_tokens[asset_name] = base_info
159
+ self.spot_tokens.setdefault(quote_name, quote_info)
160
+
161
+ def _update_coin_symbols(self) -> None:
162
+ self.coin_symbols = {
163
+ v: k for k, v in self.coin_names.items() if not k.startswith("@")
164
+ }
165
+
166
+ async def init_metas(self) -> None:
167
+ meta_task = self.info.get_perp_meta()
168
+ spot_meta_task = self.info.get_spot_meta()
169
+ all_dex_names_task = self.get_all_dex_name()
170
+
171
+ meta, spot_meta, all_dex_names = await asyncio.gather(
172
+ meta_task, spot_meta_task, all_dex_names_task
173
+ )
174
+
175
+ self._init_perp_meta(meta, 0)
176
+ self._init_spot_meta(spot_meta)
177
+
178
+ dex_meta_tasks = []
179
+ dex_indices = []
180
+ for dex in self.perp_dexs:
181
+ if dex == "":
182
+ continue
183
+ try:
184
+ idx = all_dex_names.index(dex)
185
+ except ValueError:
186
+ continue
187
+
188
+ if idx > 0:
189
+ dex_meta_tasks.append(self.info.get_perp_meta(dex))
190
+ dex_indices.append(idx)
191
+
192
+ if dex_meta_tasks:
193
+ dex_metas = await asyncio.gather(*dex_meta_tasks)
194
+ for idx, dex_meta in zip(dex_indices, dex_metas):
195
+ dex_asset_offset = PERP_DEX_OFFSET + (idx - 1) * 10000
196
+ self._init_perp_meta(dex_meta, dex_asset_offset)
197
+
198
+ self._update_coin_symbols()
199
+
200
+ async def get_metas(self, perp_only: bool = False) -> Metas:
201
+ metas: Metas = {"perp": {}, "spot": [], "dexs": {}} # type: ignore
202
+ if perp_only:
203
+ metas["perp"] = await self.info.get_perp_meta()
204
+ return metas
205
+
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
211
+ return metas
212
+
213
+ async def get_all_metas(self) -> Metas:
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
+ }
227
+ return {"perp": perp_meta, "spot": spot_meta, "dexs": dex_metas}
228
+
229
+ async def get_all_dex_name(self) -> list[str]:
230
+ names = []
231
+ dexs = await self.info.get_perp_dexs()
232
+ for dex in dexs:
233
+ if dex is None:
234
+ names.append("")
235
+ else:
236
+ names.append(dex["name"])
237
+ return names
238
+
239
+ async def get_coin_name(self, coin: str) -> str:
240
+ if not hasattr(self, "coin_names") or coin not in self.coin_names:
241
+ await self.init_metas()
242
+
243
+ if coin not in self.coin_names:
244
+ raise ValueError(f"Coin {coin} not found")
245
+
246
+ return self.coin_names[coin]
247
+
248
+ async def get_coin_asset(self, coin: str) -> int:
249
+ coin_name = await self.get_coin_name(coin)
250
+ if coin_name not in self.coin_assets:
251
+ raise ValueError(f"Coin {coin}({coin_name}) not found")
252
+
253
+ return self.coin_assets[coin_name]
254
+
255
+ async def get_coin_symbol(self, coin: str) -> str:
256
+ coin_name = await self.get_coin_name(coin)
257
+ return self.coin_symbols[coin_name]
258
+
259
+ async def get_coin_sz_decimals(self, coin: str) -> int:
260
+ coin_name = await self.get_coin_name(coin)
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]
265
+ return self.asset_sz_decimals[asset]
266
+
267
+ async def get_token_info(self, coin: str) -> SpotTokenMeta:
268
+ coin_name = await self.get_coin_name(coin)
269
+ return self.spot_tokens[coin_name]
270
+
271
+ async def get_token_id(self, coin: str) -> str:
272
+ token_info = await self.get_token_info(coin)
273
+ if not token_info:
274
+ raise ValueError(f"Token {coin} not found")
275
+
276
+ return token_info["tokenId"]