Skip to content

Commit b7b70f7

Browse files
committed
Version 0.12.0
1 parent cf2a8e0 commit b7b70f7

File tree

9 files changed

+100
-25
lines changed

9 files changed

+100
-25
lines changed

examples/evm_erc20.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ class FinalizeEvmContractAction(TypedDict):
278278
},
279279
}
280280
nonce = get_timestamp_ms()
281-
signature = sign_l1_action(account, action, None, nonce, False)
281+
signature = sign_l1_action(account, action, None, nonce, None, False)
282282
payload = {
283283
"action": action,
284284
"nonce": nonce,
@@ -299,7 +299,7 @@ class FinalizeEvmContractAction(TypedDict):
299299
else:
300300
finalize_action = {"type": "finalizeEvmContract", "token": TOKEN, "input": "firstStorageSlot"}
301301
nonce = get_timestamp_ms()
302-
signature = sign_l1_action(account, finalize_action, None, nonce, False)
302+
signature = sign_l1_action(account, finalize_action, None, nonce, None, False)
303303
payload = {
304304
"action": finalize_action,
305305
"nonce": nonce,

examples/multi_sig_order.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def main():
3838
timestamp,
3939
multi_sig_user,
4040
address,
41+
exchange.expires_after,
4142
)
4243
signatures.append(signature)
4344

examples/multi_sig_register_token.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def main():
4141
timestamp,
4242
multi_sig_user,
4343
address,
44+
exchange.expires_after,
4445
)
4546
signatures.append(signature)
4647

examples/spot_deploy.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
# Set to True to enable freeze functionality for the deployed token
1212
# See step 2-a below for more details on freezing.
1313
ENABLE_FREEZE_PRIVILEGE = False
14+
# Set to True to set the deployer trading fee share
15+
# See step 6 below for more details on setting the deployer trading fee share.
16+
SET_DEPLOYER_TRADING_FEE_SHARE = False
1417
DUMMY_USER = "0x0000000000000000000000000000000000000001"
1518

1619

@@ -101,6 +104,14 @@ def main():
101104
register_hyperliquidity_result = exchange.spot_deploy_register_hyperliquidity(spot, 2.0, 4.0, 100, None)
102105
print(register_hyperliquidity_result)
103106

107+
if SET_DEPLOYER_TRADING_FEE_SHARE:
108+
# Step 6
109+
#
110+
# Note that the deployer trading fee share cannot increase.
111+
# The default is already 100% and the smallest increment is 0.001%.
112+
set_deployer_trading_fee_share_result = exchange.spot_deploy_set_deployer_trading_fee_share(token, "100%")
113+
print(set_deployer_trading_fee_share_result)
114+
104115

105116
if __name__ == "__main__":
106117
main()

hyperliquid/exchange.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ def __init__(
5252
self.vault_address = vault_address
5353
self.account_address = account_address
5454
self.info = Info(base_url, True, meta, spot_meta)
55+
self.expires_after: Optional[int] = None
5556

5657
def _post_action(self, action, signature, nonce):
5758
payload = {
5859
"action": action,
5960
"nonce": nonce,
6061
"signature": signature,
6162
"vaultAddress": self.vault_address if action["type"] != "usdClassTransfer" else None,
63+
"expiresAfter": self.expires_after,
6264
}
6365
logging.debug(payload)
6466
return self.post("/exchange", payload)
@@ -84,6 +86,12 @@ def _slippage_price(
8486
# We round px to 5 significant figures and 6 decimals for perps, 8 decimals for spot
8587
return round(float(f"{px:.5g}"), (6 if not is_spot else 8) - self.info.asset_to_sz_decimals[asset])
8688

89+
# expires_after will cause actions to be rejected after that timestamp in milliseconds
90+
# expires_after is not supported on user_signed actions (e.g. usd_transfer) and must be None in order for those
91+
# actions to work.
92+
def set_expires_after(self, expires_after: Optional[int]) -> None:
93+
self.expires_after = expires_after
94+
8795
def order(
8896
self,
8997
name: str,
@@ -122,6 +130,7 @@ def bulk_orders(self, order_requests: List[OrderRequest], builder: Optional[Buil
122130
order_action,
123131
self.vault_address,
124132
timestamp,
133+
self.expires_after,
125134
self.base_url == MAINNET_API_URL,
126135
)
127136

@@ -176,6 +185,7 @@ def bulk_modify_orders_new(self, modify_requests: List[ModifyRequest]) -> Any:
176185
modify_action,
177186
self.vault_address,
178187
timestamp,
188+
self.expires_after,
179189
self.base_url == MAINNET_API_URL,
180190
)
181191

@@ -262,6 +272,7 @@ def bulk_cancel(self, cancel_requests: List[CancelRequest]) -> Any:
262272
cancel_action,
263273
self.vault_address,
264274
timestamp,
275+
self.expires_after,
265276
self.base_url == MAINNET_API_URL,
266277
)
267278

@@ -289,6 +300,7 @@ def bulk_cancel_by_cloid(self, cancel_requests: List[CancelByCloidRequest]) -> A
289300
cancel_action,
290301
self.vault_address,
291302
timestamp,
303+
self.expires_after,
292304
self.base_url == MAINNET_API_URL,
293305
)
294306

@@ -317,6 +329,7 @@ def schedule_cancel(self, time: Optional[int]) -> Any:
317329
schedule_cancel_action,
318330
self.vault_address,
319331
timestamp,
332+
self.expires_after,
320333
self.base_url == MAINNET_API_URL,
321334
)
322335
return self._post_action(
@@ -338,6 +351,7 @@ def update_leverage(self, leverage: int, name: str, is_cross: bool = True) -> An
338351
update_leverage_action,
339352
self.vault_address,
340353
timestamp,
354+
self.expires_after,
341355
self.base_url == MAINNET_API_URL,
342356
)
343357
return self._post_action(
@@ -360,6 +374,7 @@ def update_isolated_margin(self, amount: float, name: str) -> Any:
360374
update_isolated_margin_action,
361375
self.vault_address,
362376
timestamp,
377+
self.expires_after,
363378
self.base_url == MAINNET_API_URL,
364379
)
365380
return self._post_action(
@@ -379,6 +394,7 @@ def set_referrer(self, code: str) -> Any:
379394
set_referrer_action,
380395
None,
381396
timestamp,
397+
self.expires_after,
382398
self.base_url == MAINNET_API_URL,
383399
)
384400
return self._post_action(
@@ -398,6 +414,7 @@ def create_sub_account(self, name: str) -> Any:
398414
create_sub_account_action,
399415
None,
400416
timestamp,
417+
self.expires_after,
401418
self.base_url == MAINNET_API_URL,
402419
)
403420
return self._post_action(
@@ -438,6 +455,7 @@ def sub_account_transfer(self, sub_account_user: str, is_deposit: bool, usd: int
438455
sub_account_transfer_action,
439456
None,
440457
timestamp,
458+
self.expires_after,
441459
self.base_url == MAINNET_API_URL,
442460
)
443461
return self._post_action(
@@ -460,6 +478,7 @@ def sub_account_spot_transfer(self, sub_account_user: str, is_deposit: bool, tok
460478
sub_account_transfer_action,
461479
None,
462480
timestamp,
481+
self.expires_after,
463482
self.base_url == MAINNET_API_URL,
464483
)
465484
return self._post_action(
@@ -477,7 +496,7 @@ def vault_usd_transfer(self, vault_address: str, is_deposit: bool, usd: int) ->
477496
"usd": usd,
478497
}
479498
is_mainnet = self.base_url == MAINNET_API_URL
480-
signature = sign_l1_action(self.wallet, vault_transfer_action, None, timestamp, is_mainnet)
499+
signature = sign_l1_action(self.wallet, vault_transfer_action, None, timestamp, self.expires_after, is_mainnet)
481500
return self._post_action(
482501
vault_transfer_action,
483502
signature,
@@ -590,6 +609,7 @@ def spot_deploy_register_token(
590609
action,
591610
None,
592611
timestamp,
612+
self.expires_after,
593613
self.base_url == MAINNET_API_URL,
594614
)
595615
return self._post_action(
@@ -615,6 +635,7 @@ def spot_deploy_user_genesis(
615635
action,
616636
None,
617637
timestamp,
638+
self.expires_after,
618639
self.base_url == MAINNET_API_URL,
619640
)
620641
return self._post_action(
@@ -636,6 +657,7 @@ def spot_deploy_enable_freeze_privilege(self, token: int) -> Any:
636657
action,
637658
None,
638659
timestamp,
660+
self.expires_after,
639661
self.base_url == MAINNET_API_URL,
640662
)
641663
return self._post_action(
@@ -659,6 +681,7 @@ def spot_deploy_freeze_user(self, token: int, user: str, freeze: bool) -> Any:
659681
action,
660682
None,
661683
timestamp,
684+
self.expires_after,
662685
self.base_url == MAINNET_API_URL,
663686
)
664687
return self._post_action(
@@ -680,6 +703,7 @@ def spot_deploy_revoke_freeze_privilege(self, token: int) -> Any:
680703
action,
681704
None,
682705
timestamp,
706+
self.expires_after,
683707
self.base_url == MAINNET_API_URL,
684708
)
685709
return self._post_action(
@@ -705,6 +729,7 @@ def spot_deploy_genesis(self, token: int, max_supply: str, no_hyperliquidity: bo
705729
action,
706730
None,
707731
timestamp,
732+
self.expires_after,
708733
self.base_url == MAINNET_API_URL,
709734
)
710735
return self._post_action(
@@ -726,6 +751,7 @@ def spot_deploy_register_spot(self, base_token: int, quote_token: int) -> Any:
726751
action,
727752
None,
728753
timestamp,
754+
self.expires_after,
729755
self.base_url == MAINNET_API_URL,
730756
)
731757
return self._post_action(
@@ -755,6 +781,30 @@ def spot_deploy_register_hyperliquidity(
755781
action,
756782
None,
757783
timestamp,
784+
self.expires_after,
785+
self.base_url == MAINNET_API_URL,
786+
)
787+
return self._post_action(
788+
action,
789+
signature,
790+
timestamp,
791+
)
792+
793+
def spot_deploy_set_deployer_trading_fee_share(self, token: int, share: str) -> Any:
794+
timestamp = get_timestamp_ms()
795+
action = {
796+
"type": "spotDeploy",
797+
"setDeployerTradingFeeShare": {
798+
"token": token,
799+
"share": share,
800+
},
801+
}
802+
signature = sign_l1_action(
803+
self.wallet,
804+
action,
805+
None,
806+
timestamp,
807+
self.expires_after,
758808
self.base_url == MAINNET_API_URL,
759809
)
760810
return self._post_action(
@@ -782,6 +832,7 @@ def multi_sig(self, multi_sig_user, inner_action, signatures, nonce, vault_addre
782832
is_mainnet,
783833
vault_address,
784834
nonce,
835+
self.expires_after,
785836
)
786837
return self._post_action(
787838
multi_sig_action,
@@ -800,6 +851,7 @@ def use_big_blocks(self, enable: bool) -> Any:
800851
action,
801852
None,
802853
timestamp,
854+
self.expires_after,
803855
self.base_url == MAINNET_API_URL,
804856
)
805857
return self._post_action(

hyperliquid/utils/signing.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,23 +134,26 @@ def address_to_bytes(address):
134134
return bytes.fromhex(address[2:] if address.startswith("0x") else address)
135135

136136

137-
def action_hash(action, vault_address, nonce):
137+
def action_hash(action, vault_address, nonce, expires_after):
138138
data = msgpack.packb(action)
139139
data += nonce.to_bytes(8, "big")
140140
if vault_address is None:
141141
data += b"\x00"
142142
else:
143143
data += b"\x01"
144144
data += address_to_bytes(vault_address)
145+
if expires_after is not None:
146+
data += b"\x00"
147+
data += expires_after.to_bytes(8, "big")
145148
return keccak(data)
146149

147150

148151
def construct_phantom_agent(hash, is_mainnet):
149152
return {"source": "a" if is_mainnet else "b", "connectionId": hash}
150153

151154

152-
def sign_l1_action(wallet, action, active_pool, nonce, is_mainnet):
153-
hash = action_hash(action, active_pool, nonce)
155+
def sign_l1_action(wallet, action, active_pool, nonce, expires_after, is_mainnet):
156+
hash = action_hash(action, active_pool, nonce, expires_after)
154157
phantom_agent = construct_phantom_agent(hash, is_mainnet)
155158
data = {
156159
"domain": {
@@ -248,22 +251,23 @@ def sign_multi_sig_user_signed_action_payload(
248251

249252

250253
def sign_multi_sig_l1_action_payload(
251-
wallet, action, is_mainnet, vault_address, timestamp, payload_multi_sig_user, outer_signer
254+
wallet, action, is_mainnet, vault_address, timestamp, expires_after, payload_multi_sig_user, outer_signer
252255
):
253256
envelope = [payload_multi_sig_user.lower(), outer_signer.lower(), action]
254257
return sign_l1_action(
255258
wallet,
256259
envelope,
257260
vault_address,
258261
timestamp,
262+
expires_after,
259263
is_mainnet,
260264
)
261265

262266

263-
def sign_multi_sig_action(wallet, action, is_mainnet, vault_address, nonce):
267+
def sign_multi_sig_action(wallet, action, is_mainnet, vault_address, nonce, expires_after):
264268
action_without_tag = action.copy()
265269
del action_without_tag["type"]
266-
multi_sig_action_hash = action_hash(action_without_tag, vault_address, nonce)
270+
multi_sig_action_hash = action_hash(action_without_tag, vault_address, nonce, expires_after)
267271
envelope = {
268272
"multiSigActionHash": multi_sig_action_hash,
269273
"nonce": nonce,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
55

66
[tool.poetry]
77
name = "hyperliquid-python-sdk"
8-
version = "0.11.0"
8+
version = "0.12.0"
99
description = "SDK for Hyperliquid API trading with Python."
1010
readme = "README.md"
1111
authors = ["Hyperliquid <[email protected]>"]

tests/info_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22

33
from hyperliquid.info import Info
4-
from hyperliquid.utils.types import Meta, SpotMeta
4+
from hyperliquid.utils.types import L2BookData, Meta, SpotMeta
55

66
TEST_META: Meta = {"universe": []}
77
TEST_SPOT_META: SpotMeta = {"universe": [], "tokens": []}
@@ -83,7 +83,7 @@ def test_get_funding_history(endTime):
8383
@pytest.mark.vcr()
8484
def test_get_l2_snapshot():
8585
info = Info(skip_ws=True, spot_meta=TEST_SPOT_META)
86-
response = info.l2_snapshot(name="DYDX")
86+
response: L2BookData = info.l2_snapshot(name="DYDX")
8787
assert len(response) != 0
8888
assert len(response["levels"]) == 2
8989
assert response["coin"] == "DYDX"

0 commit comments

Comments
 (0)