hyperliquid-python-sdk-async 0.24.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,888 @@
1
+ import asyncio
2
+ import json
3
+ import logging
4
+ import secrets
5
+
6
+ import eth_account
7
+ from eth_account.signers.local import LocalAccount
8
+
9
+ from hyperliquid.api import API
10
+ from hyperliquid.info import Info
11
+ from hyperliquid.utils.constants import MAINNET_API_URL
12
+ from hyperliquid.utils.signing import (
13
+ CancelByCloidRequest,
14
+ CancelRequest,
15
+ Grouping,
16
+ ModifyRequest,
17
+ OidOrCloid,
18
+ OrderRequest,
19
+ OrderType,
20
+ OrderWire,
21
+ ScheduleCancelAction,
22
+ float_to_usd_int,
23
+ get_timestamp_ms,
24
+ order_request_to_order_wire,
25
+ order_wires_to_order_action,
26
+ sign_agent,
27
+ sign_approve_builder_fee,
28
+ sign_convert_to_multi_sig_user_action,
29
+ sign_l1_action,
30
+ sign_multi_sig_action,
31
+ sign_send_asset_action,
32
+ sign_spot_transfer_action,
33
+ sign_token_delegate_action,
34
+ sign_usd_class_transfer_action,
35
+ sign_usd_transfer_action,
36
+ sign_user_dex_abstraction_action,
37
+ sign_user_set_abstraction_action,
38
+ sign_withdraw_from_bridge_action,
39
+ )
40
+ from hyperliquid.utils.types import (
41
+ Abstraction,
42
+ AgentAbstraction,
43
+ Any,
44
+ BuilderInfo,
45
+ Cloid,
46
+ Dict,
47
+ List,
48
+ Meta,
49
+ Optional,
50
+ PerpDexSchemaInput,
51
+ SpotMeta,
52
+ Tuple,
53
+ )
54
+
55
+
56
+ def _get_dex(coin: str) -> str:
57
+ return coin.split(":")[0] if ":" in coin else ""
58
+
59
+
60
+ USER_SET_ABSTRACTION_WIRE_VALUES = {
61
+ "disabled": "i",
62
+ "unifiedAccount": "u",
63
+ "portfolioMargin": "p",
64
+ }
65
+
66
+
67
+ def _multi_sig_payload_action(inner_action):
68
+ if inner_action.get("type") != "userSetAbstraction":
69
+ return inner_action
70
+
71
+ abstraction = inner_action.get("abstraction")
72
+ if abstraction not in USER_SET_ABSTRACTION_WIRE_VALUES:
73
+ return inner_action
74
+
75
+ payload_action = inner_action.copy()
76
+ payload_action["abstraction"] = USER_SET_ABSTRACTION_WIRE_VALUES[abstraction]
77
+ return payload_action
78
+
79
+
80
+ class Exchange(API):
81
+ DEFAULT_SLIPPAGE = 0.05
82
+
83
+ def __init__(
84
+ self,
85
+ wallet: LocalAccount,
86
+ base_url: Optional[str] = None,
87
+ meta: Optional[Meta] = None,
88
+ vault_address: Optional[str] = None,
89
+ account_address: Optional[str] = None,
90
+ spot_meta: Optional[SpotMeta] = None,
91
+ perp_dexs: Optional[List[str]] = None,
92
+ timeout: Optional[float] = None,
93
+ session=None,
94
+ ):
95
+ super().__init__(base_url, timeout, session=session)
96
+ self.wallet = wallet
97
+ self.vault_address = vault_address
98
+ self.account_address = account_address
99
+ self.info = Info(base_url, True, meta, spot_meta, perp_dexs, timeout, session=session)
100
+ self.expires_after: Optional[int] = None
101
+ self._initialized = False
102
+ self._init_lock = asyncio.Lock()
103
+
104
+ @classmethod
105
+ async def create(cls, *args, **kwargs) -> "Exchange":
106
+ client = cls(*args, **kwargs)
107
+ await client.initialize()
108
+ return client
109
+
110
+ async def initialize(self) -> "Exchange":
111
+ if self._initialized:
112
+ return self
113
+
114
+ async with self._init_lock:
115
+ if self._initialized:
116
+ return self
117
+ session = await self._ensure_session()
118
+ self.info.session = session
119
+ self.info._owns_session = False
120
+ await self.info.initialize()
121
+ self._initialized = True
122
+ return self
123
+
124
+ async def _ensure_initialized(self) -> None:
125
+ if not self._initialized:
126
+ await self.initialize()
127
+
128
+ async def aclose(self) -> None:
129
+ await self.info.aclose()
130
+ await super().aclose()
131
+
132
+ async def _post_action(self, action, signature, nonce):
133
+ payload = {
134
+ "action": action,
135
+ "nonce": nonce,
136
+ "signature": signature,
137
+ "vaultAddress": self.vault_address if action["type"] not in ["usdClassTransfer", "sendAsset"] else None,
138
+ "expiresAfter": self.expires_after,
139
+ }
140
+ logging.debug(payload)
141
+ return await self.post("/exchange", payload)
142
+
143
+ async def _slippage_price(
144
+ self,
145
+ name: str,
146
+ is_buy: bool,
147
+ slippage: float,
148
+ px: Optional[float] = None,
149
+ ) -> float:
150
+ await self._ensure_initialized()
151
+ coin = self.info.name_to_coin[name]
152
+ if not px:
153
+ dex = _get_dex(coin)
154
+ px = float((await self.info.all_mids(dex))[coin])
155
+
156
+ asset = self.info.coin_to_asset[coin]
157
+ is_spot = asset >= 10_000
158
+ px *= (1 + slippage) if is_buy else (1 - slippage)
159
+ return round(float(f"{px:.5g}"), (6 if not is_spot else 8) - self.info.asset_to_sz_decimals[asset])
160
+
161
+ def set_expires_after(self, expires_after: Optional[int]) -> None:
162
+ self.expires_after = expires_after
163
+
164
+ async def order(
165
+ self,
166
+ name: str,
167
+ is_buy: bool,
168
+ sz: float,
169
+ limit_px: float,
170
+ order_type: OrderType,
171
+ reduce_only: bool = False,
172
+ cloid: Optional[Cloid] = None,
173
+ builder: Optional[BuilderInfo] = None,
174
+ ) -> Any:
175
+ order: OrderRequest = {
176
+ "coin": name,
177
+ "is_buy": is_buy,
178
+ "sz": sz,
179
+ "limit_px": limit_px,
180
+ "order_type": order_type,
181
+ "reduce_only": reduce_only,
182
+ }
183
+ if cloid:
184
+ order["cloid"] = cloid
185
+ return await self.bulk_orders([order], builder)
186
+
187
+ async def bulk_orders(
188
+ self, order_requests: List[OrderRequest], builder: Optional[BuilderInfo] = None, grouping: Grouping = "na"
189
+ ) -> Any:
190
+ await self._ensure_initialized()
191
+ order_wires: List[OrderWire] = [
192
+ order_request_to_order_wire(order, await self.info.name_to_asset(order["coin"])) for order in order_requests
193
+ ]
194
+ timestamp = get_timestamp_ms()
195
+
196
+ if builder:
197
+ builder["b"] = builder["b"].lower()
198
+ order_action = order_wires_to_order_action(order_wires, builder, grouping)
199
+
200
+ signature = sign_l1_action(
201
+ self.wallet,
202
+ order_action,
203
+ self.vault_address,
204
+ timestamp,
205
+ self.expires_after,
206
+ self.base_url == MAINNET_API_URL,
207
+ )
208
+
209
+ return await self._post_action(order_action, signature, timestamp)
210
+
211
+ async def modify_order(
212
+ self,
213
+ oid: OidOrCloid,
214
+ name: str,
215
+ is_buy: bool,
216
+ sz: float,
217
+ limit_px: float,
218
+ order_type: OrderType,
219
+ reduce_only: bool = False,
220
+ cloid: Optional[Cloid] = None,
221
+ ) -> Any:
222
+ modify: ModifyRequest = {
223
+ "oid": oid,
224
+ "order": {
225
+ "coin": name,
226
+ "is_buy": is_buy,
227
+ "sz": sz,
228
+ "limit_px": limit_px,
229
+ "order_type": order_type,
230
+ "reduce_only": reduce_only,
231
+ "cloid": cloid,
232
+ },
233
+ }
234
+ return await self.bulk_modify_orders_new([modify])
235
+
236
+ async def bulk_modify_orders_new(self, modify_requests: List[ModifyRequest]) -> Any:
237
+ await self._ensure_initialized()
238
+ timestamp = get_timestamp_ms()
239
+ modify_wires = [
240
+ {
241
+ "oid": modify["oid"].to_raw() if isinstance(modify["oid"], Cloid) else modify["oid"],
242
+ "order": order_request_to_order_wire(
243
+ modify["order"], await self.info.name_to_asset(modify["order"]["coin"])
244
+ ),
245
+ }
246
+ for modify in modify_requests
247
+ ]
248
+
249
+ modify_action = {
250
+ "type": "batchModify",
251
+ "modifies": modify_wires,
252
+ }
253
+
254
+ signature = sign_l1_action(
255
+ self.wallet,
256
+ modify_action,
257
+ self.vault_address,
258
+ timestamp,
259
+ self.expires_after,
260
+ self.base_url == MAINNET_API_URL,
261
+ )
262
+
263
+ return await self._post_action(modify_action, signature, timestamp)
264
+
265
+ async def market_open(
266
+ self,
267
+ name: str,
268
+ is_buy: bool,
269
+ sz: float,
270
+ px: Optional[float] = None,
271
+ slippage: float = DEFAULT_SLIPPAGE,
272
+ cloid: Optional[Cloid] = None,
273
+ builder: Optional[BuilderInfo] = None,
274
+ ) -> Any:
275
+ px = await self._slippage_price(name, is_buy, slippage, px)
276
+ return await self.order(
277
+ name, is_buy, sz, px, order_type={"limit": {"tif": "Ioc"}}, reduce_only=False, cloid=cloid, builder=builder
278
+ )
279
+
280
+ async def market_close(
281
+ self,
282
+ coin: str,
283
+ sz: Optional[float] = None,
284
+ px: Optional[float] = None,
285
+ slippage: float = DEFAULT_SLIPPAGE,
286
+ cloid: Optional[Cloid] = None,
287
+ builder: Optional[BuilderInfo] = None,
288
+ ) -> Any:
289
+ await self._ensure_initialized()
290
+ address: str = self.wallet.address
291
+ if self.account_address:
292
+ address = self.account_address
293
+ if self.vault_address:
294
+ address = self.vault_address
295
+ dex = _get_dex(coin)
296
+ positions = (await self.info.user_state(address, dex))["assetPositions"]
297
+ for position in positions:
298
+ item = position["position"]
299
+ if coin != item["coin"]:
300
+ continue
301
+ szi = float(item["szi"])
302
+ if not sz:
303
+ sz = abs(szi)
304
+ is_buy = szi < 0
305
+ px = await self._slippage_price(coin, is_buy, slippage, px)
306
+ return await self.order(
307
+ coin,
308
+ is_buy,
309
+ sz,
310
+ px,
311
+ order_type={"limit": {"tif": "Ioc"}},
312
+ reduce_only=True,
313
+ cloid=cloid,
314
+ builder=builder,
315
+ )
316
+ return None
317
+
318
+ async def cancel(self, name: str, oid: int) -> Any:
319
+ return await self.bulk_cancel([{"coin": name, "oid": oid}])
320
+
321
+ async def cancel_by_cloid(self, name: str, cloid: Cloid) -> Any:
322
+ return await self.bulk_cancel_by_cloid([{"coin": name, "cloid": cloid}])
323
+
324
+ async def bulk_cancel(self, cancel_requests: List[CancelRequest]) -> Any:
325
+ await self._ensure_initialized()
326
+ timestamp = get_timestamp_ms()
327
+ cancel_action = {
328
+ "type": "cancel",
329
+ "cancels": [{"a": await self.info.name_to_asset(cancel["coin"]), "o": cancel["oid"]} for cancel in cancel_requests],
330
+ }
331
+ signature = sign_l1_action(
332
+ self.wallet,
333
+ cancel_action,
334
+ self.vault_address,
335
+ timestamp,
336
+ self.expires_after,
337
+ self.base_url == MAINNET_API_URL,
338
+ )
339
+ return await self._post_action(cancel_action, signature, timestamp)
340
+
341
+ async def bulk_cancel_by_cloid(self, cancel_requests: List[CancelByCloidRequest]) -> Any:
342
+ await self._ensure_initialized()
343
+ timestamp = get_timestamp_ms()
344
+ cancel_action = {
345
+ "type": "cancelByCloid",
346
+ "cancels": [
347
+ {"asset": await self.info.name_to_asset(cancel["coin"]), "cloid": cancel["cloid"].to_raw()}
348
+ for cancel in cancel_requests
349
+ ],
350
+ }
351
+ signature = sign_l1_action(
352
+ self.wallet,
353
+ cancel_action,
354
+ self.vault_address,
355
+ timestamp,
356
+ self.expires_after,
357
+ self.base_url == MAINNET_API_URL,
358
+ )
359
+ return await self._post_action(cancel_action, signature, timestamp)
360
+
361
+ async def schedule_cancel(self, time: Optional[int]) -> Any:
362
+ timestamp = get_timestamp_ms()
363
+ schedule_cancel_action: ScheduleCancelAction = {"type": "scheduleCancel"}
364
+ if time is not None:
365
+ schedule_cancel_action["time"] = time
366
+ signature = sign_l1_action(
367
+ self.wallet,
368
+ schedule_cancel_action,
369
+ self.vault_address,
370
+ timestamp,
371
+ self.expires_after,
372
+ self.base_url == MAINNET_API_URL,
373
+ )
374
+ return await self._post_action(schedule_cancel_action, signature, timestamp)
375
+
376
+ async def update_leverage(self, leverage: int, name: str, is_cross: bool = True) -> Any:
377
+ await self._ensure_initialized()
378
+ timestamp = get_timestamp_ms()
379
+ update_leverage_action = {
380
+ "type": "updateLeverage",
381
+ "asset": await self.info.name_to_asset(name),
382
+ "isCross": is_cross,
383
+ "leverage": leverage,
384
+ }
385
+ signature = sign_l1_action(
386
+ self.wallet,
387
+ update_leverage_action,
388
+ self.vault_address,
389
+ timestamp,
390
+ self.expires_after,
391
+ self.base_url == MAINNET_API_URL,
392
+ )
393
+ return await self._post_action(update_leverage_action, signature, timestamp)
394
+
395
+ async def update_isolated_margin(self, amount: float, name: str) -> Any:
396
+ await self._ensure_initialized()
397
+ timestamp = get_timestamp_ms()
398
+ amount = float_to_usd_int(amount)
399
+ update_isolated_margin_action = {
400
+ "type": "updateIsolatedMargin",
401
+ "asset": await self.info.name_to_asset(name),
402
+ "isBuy": True,
403
+ "ntli": amount,
404
+ }
405
+ signature = sign_l1_action(
406
+ self.wallet,
407
+ update_isolated_margin_action,
408
+ self.vault_address,
409
+ timestamp,
410
+ self.expires_after,
411
+ self.base_url == MAINNET_API_URL,
412
+ )
413
+ return await self._post_action(update_isolated_margin_action, signature, timestamp)
414
+
415
+ async def set_referrer(self, code: str) -> Any:
416
+ timestamp = get_timestamp_ms()
417
+ set_referrer_action = {"type": "setReferrer", "code": code}
418
+ signature = sign_l1_action(
419
+ self.wallet,
420
+ set_referrer_action,
421
+ None,
422
+ timestamp,
423
+ self.expires_after,
424
+ self.base_url == MAINNET_API_URL,
425
+ )
426
+ return await self._post_action(set_referrer_action, signature, timestamp)
427
+
428
+ async def create_sub_account(self, name: str) -> Any:
429
+ timestamp = get_timestamp_ms()
430
+ create_sub_account_action = {"type": "createSubAccount", "name": name}
431
+ signature = sign_l1_action(
432
+ self.wallet,
433
+ create_sub_account_action,
434
+ None,
435
+ timestamp,
436
+ self.expires_after,
437
+ self.base_url == MAINNET_API_URL,
438
+ )
439
+ return await self._post_action(create_sub_account_action, signature, timestamp)
440
+
441
+ async def usd_class_transfer(self, amount: float, to_perp: bool) -> Any:
442
+ timestamp = get_timestamp_ms()
443
+ str_amount = str(amount)
444
+ if self.vault_address:
445
+ str_amount += f" subaccount:{self.vault_address}"
446
+
447
+ action = {"type": "usdClassTransfer", "amount": str_amount, "toPerp": to_perp, "nonce": timestamp}
448
+ signature = sign_usd_class_transfer_action(self.wallet, action, self.base_url == MAINNET_API_URL)
449
+ return await self._post_action(action, signature, timestamp)
450
+
451
+ async def send_asset(self, destination: str, source_dex: str, destination_dex: str, token: str, amount: float) -> Any:
452
+ timestamp = get_timestamp_ms()
453
+ action = {
454
+ "type": "sendAsset",
455
+ "destination": destination,
456
+ "sourceDex": source_dex,
457
+ "destinationDex": destination_dex,
458
+ "token": token,
459
+ "amount": str(amount),
460
+ "fromSubAccount": self.vault_address if self.vault_address else "",
461
+ "nonce": timestamp,
462
+ }
463
+ signature = sign_send_asset_action(self.wallet, action, self.base_url == MAINNET_API_URL)
464
+ return await self._post_action(action, signature, timestamp)
465
+
466
+ async def sub_account_transfer(self, sub_account_user: str, is_deposit: bool, usd: int) -> Any:
467
+ timestamp = get_timestamp_ms()
468
+ sub_account_transfer_action = {
469
+ "type": "subAccountTransfer",
470
+ "subAccountUser": sub_account_user,
471
+ "isDeposit": is_deposit,
472
+ "usd": usd,
473
+ }
474
+ signature = sign_l1_action(
475
+ self.wallet,
476
+ sub_account_transfer_action,
477
+ None,
478
+ timestamp,
479
+ self.expires_after,
480
+ self.base_url == MAINNET_API_URL,
481
+ )
482
+ return await self._post_action(sub_account_transfer_action, signature, timestamp)
483
+
484
+ async def sub_account_spot_transfer(self, sub_account_user: str, is_deposit: bool, token: str, amount: float) -> Any:
485
+ timestamp = get_timestamp_ms()
486
+ sub_account_transfer_action = {
487
+ "type": "subAccountSpotTransfer",
488
+ "subAccountUser": sub_account_user,
489
+ "isDeposit": is_deposit,
490
+ "token": token,
491
+ "amount": str(amount),
492
+ }
493
+ signature = sign_l1_action(
494
+ self.wallet,
495
+ sub_account_transfer_action,
496
+ None,
497
+ timestamp,
498
+ self.expires_after,
499
+ self.base_url == MAINNET_API_URL,
500
+ )
501
+ return await self._post_action(sub_account_transfer_action, signature, timestamp)
502
+
503
+ async def vault_usd_transfer(self, vault_address: str, is_deposit: bool, usd: int) -> Any:
504
+ timestamp = get_timestamp_ms()
505
+ vault_transfer_action = {
506
+ "type": "vaultTransfer",
507
+ "vaultAddress": vault_address,
508
+ "isDeposit": is_deposit,
509
+ "usd": usd,
510
+ }
511
+ is_mainnet = self.base_url == MAINNET_API_URL
512
+ signature = sign_l1_action(self.wallet, vault_transfer_action, None, timestamp, self.expires_after, is_mainnet)
513
+ return await self._post_action(vault_transfer_action, signature, timestamp)
514
+
515
+ async def usd_transfer(self, amount: float, destination: str) -> Any:
516
+ timestamp = get_timestamp_ms()
517
+ action = {"destination": destination, "amount": str(amount), "time": timestamp, "type": "usdSend"}
518
+ is_mainnet = self.base_url == MAINNET_API_URL
519
+ signature = sign_usd_transfer_action(self.wallet, action, is_mainnet)
520
+ return await self._post_action(action, signature, timestamp)
521
+
522
+ async def spot_transfer(self, amount: float, destination: str, token: str) -> Any:
523
+ timestamp = get_timestamp_ms()
524
+ action = {"destination": destination, "amount": str(amount), "token": token, "time": timestamp, "type": "spotSend"}
525
+ is_mainnet = self.base_url == MAINNET_API_URL
526
+ signature = sign_spot_transfer_action(self.wallet, action, is_mainnet)
527
+ return await self._post_action(action, signature, timestamp)
528
+
529
+ async def token_delegate(self, validator: str, wei: int, is_undelegate: bool) -> Any:
530
+ timestamp = get_timestamp_ms()
531
+ action = {
532
+ "validator": validator,
533
+ "wei": wei,
534
+ "isUndelegate": is_undelegate,
535
+ "nonce": timestamp,
536
+ "type": "tokenDelegate",
537
+ }
538
+ is_mainnet = self.base_url == MAINNET_API_URL
539
+ signature = sign_token_delegate_action(self.wallet, action, is_mainnet)
540
+ return await self._post_action(action, signature, timestamp)
541
+
542
+ async def withdraw_from_bridge(self, amount: float, destination: str) -> Any:
543
+ timestamp = get_timestamp_ms()
544
+ action = {"destination": destination, "amount": str(amount), "time": timestamp, "type": "withdraw3"}
545
+ is_mainnet = self.base_url == MAINNET_API_URL
546
+ signature = sign_withdraw_from_bridge_action(self.wallet, action, is_mainnet)
547
+ return await self._post_action(action, signature, timestamp)
548
+
549
+ async def approve_agent(self, name: Optional[str] = None) -> Tuple[Any, str]:
550
+ agent_key = "0x" + secrets.token_hex(32)
551
+ account = eth_account.Account.from_key(agent_key)
552
+ timestamp = get_timestamp_ms()
553
+ is_mainnet = self.base_url == MAINNET_API_URL
554
+ action = {
555
+ "type": "approveAgent",
556
+ "agentAddress": account.address,
557
+ "agentName": name or "",
558
+ "nonce": timestamp,
559
+ }
560
+ signature = sign_agent(self.wallet, action, is_mainnet)
561
+ if name is None:
562
+ del action["agentName"]
563
+ return await self._post_action(action, signature, timestamp), agent_key
564
+
565
+ async def approve_builder_fee(self, builder: str, max_fee_rate: str) -> Any:
566
+ timestamp = get_timestamp_ms()
567
+ action = {"maxFeeRate": max_fee_rate, "builder": builder, "nonce": timestamp, "type": "approveBuilderFee"}
568
+ signature = sign_approve_builder_fee(self.wallet, action, self.base_url == MAINNET_API_URL)
569
+ return await self._post_action(action, signature, timestamp)
570
+
571
+ async def convert_to_multi_sig_user(self, authorized_users: List[str], threshold: int) -> Any:
572
+ timestamp = get_timestamp_ms()
573
+ signers = {"authorizedUsers": sorted(authorized_users), "threshold": threshold}
574
+ action = {"type": "convertToMultiSigUser", "signers": json.dumps(signers), "nonce": timestamp}
575
+ signature = sign_convert_to_multi_sig_user_action(self.wallet, action, self.base_url == MAINNET_API_URL)
576
+ return await self._post_action(action, signature, timestamp)
577
+
578
+ async def spot_deploy_register_token(
579
+ self, token_name: str, sz_decimals: int, wei_decimals: int, max_gas: int, full_name: str
580
+ ) -> Any:
581
+ timestamp = get_timestamp_ms()
582
+ action = {
583
+ "type": "spotDeploy",
584
+ "registerToken2": {
585
+ "spec": {"name": token_name, "szDecimals": sz_decimals, "weiDecimals": wei_decimals},
586
+ "maxGas": max_gas,
587
+ "fullName": full_name,
588
+ },
589
+ }
590
+ signature = sign_l1_action(
591
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
592
+ )
593
+ return await self._post_action(action, signature, timestamp)
594
+
595
+ async def spot_deploy_user_genesis(
596
+ self, token: int, user_and_wei: List[Tuple[str, str]], existing_token_and_wei: List[Tuple[int, str]]
597
+ ) -> Any:
598
+ timestamp = get_timestamp_ms()
599
+ action = {
600
+ "type": "spotDeploy",
601
+ "userGenesis": {
602
+ "token": token,
603
+ "userAndWei": [(user.lower(), wei) for (user, wei) in user_and_wei],
604
+ "existingTokenAndWei": existing_token_and_wei,
605
+ },
606
+ }
607
+ signature = sign_l1_action(
608
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
609
+ )
610
+ return await self._post_action(action, signature, timestamp)
611
+
612
+ async def spot_deploy_enable_freeze_privilege(self, token: int) -> Any:
613
+ return await self.spot_deploy_token_action_inner("enableFreezePrivilege", token)
614
+
615
+ async def spot_deploy_freeze_user(self, token: int, user: str, freeze: bool) -> Any:
616
+ timestamp = get_timestamp_ms()
617
+ action = {"type": "spotDeploy", "freezeUser": {"token": token, "user": user.lower(), "freeze": freeze}}
618
+ signature = sign_l1_action(
619
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
620
+ )
621
+ return await self._post_action(action, signature, timestamp)
622
+
623
+ async def spot_deploy_revoke_freeze_privilege(self, token: int) -> Any:
624
+ return await self.spot_deploy_token_action_inner("revokeFreezePrivilege", token)
625
+
626
+ async def spot_deploy_enable_quote_token(self, token: int) -> Any:
627
+ return await self.spot_deploy_token_action_inner("enableQuoteToken", token)
628
+
629
+ async def spot_deploy_token_action_inner(self, variant: str, token: int) -> Any:
630
+ timestamp = get_timestamp_ms()
631
+ action = {"type": "spotDeploy", variant: {"token": token}}
632
+ signature = sign_l1_action(
633
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
634
+ )
635
+ return await self._post_action(action, signature, timestamp)
636
+
637
+ async def spot_deploy_genesis(self, token: int, max_supply: str, no_hyperliquidity: bool) -> Any:
638
+ timestamp = get_timestamp_ms()
639
+ genesis = {"token": token, "maxSupply": max_supply}
640
+ if no_hyperliquidity:
641
+ genesis["noHyperliquidity"] = True
642
+ action = {"type": "spotDeploy", "genesis": genesis}
643
+ signature = sign_l1_action(
644
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
645
+ )
646
+ return await self._post_action(action, signature, timestamp)
647
+
648
+ async def spot_deploy_register_spot(self, base_token: int, quote_token: int) -> Any:
649
+ timestamp = get_timestamp_ms()
650
+ action = {"type": "spotDeploy", "registerSpot": {"tokens": [base_token, quote_token]}}
651
+ signature = sign_l1_action(
652
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
653
+ )
654
+ return await self._post_action(action, signature, timestamp)
655
+
656
+ async def spot_deploy_register_hyperliquidity(
657
+ self, spot: int, start_px: float, order_sz: float, n_orders: int, n_seeded_levels: Optional[int]
658
+ ) -> Any:
659
+ timestamp = get_timestamp_ms()
660
+ register_hyperliquidity = {"spot": spot, "startPx": str(start_px), "orderSz": str(order_sz), "nOrders": n_orders}
661
+ if n_seeded_levels is not None:
662
+ register_hyperliquidity["nSeededLevels"] = n_seeded_levels
663
+ action = {"type": "spotDeploy", "registerHyperliquidity": register_hyperliquidity}
664
+ signature = sign_l1_action(
665
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
666
+ )
667
+ return await self._post_action(action, signature, timestamp)
668
+
669
+ async def spot_deploy_set_deployer_trading_fee_share(self, token: int, share: str) -> Any:
670
+ timestamp = get_timestamp_ms()
671
+ action = {"type": "spotDeploy", "setDeployerTradingFeeShare": {"token": token, "share": share}}
672
+ signature = sign_l1_action(
673
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
674
+ )
675
+ return await self._post_action(action, signature, timestamp)
676
+
677
+ async def perp_deploy_register_asset(
678
+ self,
679
+ dex: str,
680
+ max_gas: Optional[int],
681
+ coin: str,
682
+ sz_decimals: int,
683
+ oracle_px: str,
684
+ margin_table_id: int,
685
+ only_isolated: bool,
686
+ schema: Optional[PerpDexSchemaInput],
687
+ ) -> Any:
688
+ timestamp = get_timestamp_ms()
689
+ schema_wire = None
690
+ if schema is not None:
691
+ schema_wire = {
692
+ "fullName": schema["fullName"],
693
+ "collateralToken": schema["collateralToken"],
694
+ "oracleUpdater": schema["oracleUpdater"].lower() if schema["oracleUpdater"] is not None else None,
695
+ }
696
+ action = {
697
+ "type": "perpDeploy",
698
+ "registerAsset": {
699
+ "maxGas": max_gas,
700
+ "assetRequest": {
701
+ "coin": coin,
702
+ "szDecimals": sz_decimals,
703
+ "oraclePx": oracle_px,
704
+ "marginTableId": margin_table_id,
705
+ "onlyIsolated": only_isolated,
706
+ },
707
+ "dex": dex,
708
+ "schema": schema_wire,
709
+ },
710
+ }
711
+ signature = sign_l1_action(
712
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
713
+ )
714
+ return await self._post_action(action, signature, timestamp)
715
+
716
+ async def perp_deploy_set_oracle(
717
+ self,
718
+ dex: str,
719
+ oracle_pxs: Dict[str, str],
720
+ all_mark_pxs: List[Dict[str, str]],
721
+ external_perp_pxs: Dict[str, str],
722
+ ) -> Any:
723
+ timestamp = get_timestamp_ms()
724
+ action = {
725
+ "type": "perpDeploy",
726
+ "setOracle": {
727
+ "dex": dex,
728
+ "oraclePxs": sorted(list(oracle_pxs.items())),
729
+ "markPxs": [sorted(list(mark_pxs.items())) for mark_pxs in all_mark_pxs],
730
+ "externalPerpPxs": sorted(list(external_perp_pxs.items())),
731
+ },
732
+ }
733
+ signature = sign_l1_action(
734
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
735
+ )
736
+ return await self._post_action(action, signature, timestamp)
737
+
738
+ async def c_signer_unjail_self(self) -> Any:
739
+ return await self.c_signer_inner("unjailSelf")
740
+
741
+ async def c_signer_jail_self(self) -> Any:
742
+ return await self.c_signer_inner("jailSelf")
743
+
744
+ async def c_signer_inner(self, variant: str) -> Any:
745
+ timestamp = get_timestamp_ms()
746
+ action = {"type": "CSignerAction", variant: None}
747
+ signature = sign_l1_action(
748
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
749
+ )
750
+ return await self._post_action(action, signature, timestamp)
751
+
752
+ async def c_validator_register(
753
+ self,
754
+ node_ip: str,
755
+ name: str,
756
+ description: str,
757
+ delegations_disabled: bool,
758
+ commission_bps: int,
759
+ signer: str,
760
+ unjailed: bool,
761
+ initial_wei: int,
762
+ ) -> Any:
763
+ timestamp = get_timestamp_ms()
764
+ action = {
765
+ "type": "CValidatorAction",
766
+ "register": {
767
+ "profile": {
768
+ "node_ip": {"Ip": node_ip},
769
+ "name": name,
770
+ "description": description,
771
+ "delegations_disabled": delegations_disabled,
772
+ "commission_bps": commission_bps,
773
+ "signer": signer,
774
+ },
775
+ "unjailed": unjailed,
776
+ "initial_wei": initial_wei,
777
+ },
778
+ }
779
+ signature = sign_l1_action(
780
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
781
+ )
782
+ return await self._post_action(action, signature, timestamp)
783
+
784
+ async def c_validator_change_profile(
785
+ self,
786
+ node_ip: Optional[str],
787
+ name: Optional[str],
788
+ description: Optional[str],
789
+ unjailed: bool,
790
+ disable_delegations: Optional[bool],
791
+ commission_bps: Optional[int],
792
+ signer: Optional[str],
793
+ ) -> Any:
794
+ timestamp = get_timestamp_ms()
795
+ action = {
796
+ "type": "CValidatorAction",
797
+ "changeProfile": {
798
+ "node_ip": None if node_ip is None else {"Ip": node_ip},
799
+ "name": name,
800
+ "description": description,
801
+ "unjailed": unjailed,
802
+ "disable_delegations": disable_delegations,
803
+ "commission_bps": commission_bps,
804
+ "signer": signer,
805
+ },
806
+ }
807
+ signature = sign_l1_action(
808
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
809
+ )
810
+ return await self._post_action(action, signature, timestamp)
811
+
812
+ async def c_validator_unregister(self) -> Any:
813
+ timestamp = get_timestamp_ms()
814
+ action = {"type": "CValidatorAction", "unregister": None}
815
+ signature = sign_l1_action(
816
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
817
+ )
818
+ return await self._post_action(action, signature, timestamp)
819
+
820
+ async def multi_sig(self, multi_sig_user, inner_action, signatures, nonce, vault_address=None):
821
+ multi_sig_user = multi_sig_user.lower()
822
+ payload_action = _multi_sig_payload_action(inner_action)
823
+ multi_sig_action = {
824
+ "type": "multiSig",
825
+ "signatureChainId": "0x66eee",
826
+ "signatures": signatures,
827
+ "payload": {
828
+ "multiSigUser": multi_sig_user,
829
+ "outerSigner": self.wallet.address.lower(),
830
+ "action": payload_action,
831
+ },
832
+ }
833
+ is_mainnet = self.base_url == MAINNET_API_URL
834
+ signature = sign_multi_sig_action(
835
+ self.wallet, multi_sig_action, is_mainnet, vault_address, nonce, self.expires_after
836
+ )
837
+ return await self._post_action(multi_sig_action, signature, nonce)
838
+
839
+ async def use_big_blocks(self, enable: bool) -> Any:
840
+ timestamp = get_timestamp_ms()
841
+ action = {"type": "evmUserModify", "usingBigBlocks": enable}
842
+ signature = sign_l1_action(
843
+ self.wallet, action, None, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
844
+ )
845
+ return await self._post_action(action, signature, timestamp)
846
+
847
+ async def agent_enable_dex_abstraction(self) -> Any:
848
+ timestamp = get_timestamp_ms()
849
+ action = {"type": "agentEnableDexAbstraction"}
850
+ signature = sign_l1_action(
851
+ self.wallet, action, self.vault_address, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
852
+ )
853
+ return await self._post_action(action, signature, timestamp)
854
+
855
+ async def agent_set_abstraction(self, abstraction: AgentAbstraction) -> Any:
856
+ timestamp = get_timestamp_ms()
857
+ action = {"type": "agentSetAbstraction", "abstraction": abstraction}
858
+ signature = sign_l1_action(
859
+ self.wallet, action, self.vault_address, timestamp, self.expires_after, self.base_url == MAINNET_API_URL
860
+ )
861
+ return await self._post_action(action, signature, timestamp)
862
+
863
+ async def user_dex_abstraction(self, user: str, enabled: bool) -> Any:
864
+ timestamp = get_timestamp_ms()
865
+ action = {"type": "userDexAbstraction", "user": user.lower(), "enabled": enabled, "nonce": timestamp}
866
+ signature = sign_user_dex_abstraction_action(self.wallet, action, self.base_url == MAINNET_API_URL)
867
+ return await self._post_action(action, signature, timestamp)
868
+
869
+ async def user_set_abstraction(self, user: str, abstraction: Abstraction) -> Any:
870
+ timestamp = get_timestamp_ms()
871
+ action = {"type": "userSetAbstraction", "user": user.lower(), "abstraction": abstraction, "nonce": timestamp}
872
+ signature = sign_user_set_abstraction_action(self.wallet, action, self.base_url == MAINNET_API_URL)
873
+ return await self._post_action(action, signature, timestamp)
874
+
875
+ async def noop(self, nonce):
876
+ action = {"type": "noop"}
877
+ signature = sign_l1_action(
878
+ self.wallet, action, self.vault_address, nonce, self.expires_after, self.base_url == MAINNET_API_URL
879
+ )
880
+ return await self._post_action(action, signature, nonce)
881
+
882
+ async def gossip_priority_bid(self, slot_id, ip, max_gas):
883
+ nonce = get_timestamp_ms()
884
+ action = {"type": "gossipPriorityBid", "slotId": slot_id, "ip": ip, "maxGas": max_gas}
885
+ signature = sign_l1_action(
886
+ self.wallet, action, self.vault_address, nonce, self.expires_after, self.base_url == MAINNET_API_URL
887
+ )
888
+ return await self._post_action(action, signature, nonce)