Skip to content

Commit 9527b55

Browse files
authored
Merge branch 'master' into master
2 parents ab920d9 + 5379e4a commit 9527b55

18 files changed

+794
-56
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# This example shows how to place and query orders for a builder-deployed perp dex
2+
import json
3+
4+
import example_utils
5+
6+
from hyperliquid.utils import constants
7+
8+
DUMMY_DEX = "test"
9+
COIN = f"{DUMMY_DEX}:ABC"
10+
11+
12+
def main():
13+
# Supply the builder-deployed perps dex as an argument
14+
address, info, exchange = example_utils.setup(
15+
base_url=constants.TESTNET_API_URL, skip_ws=True, perp_dexs=[DUMMY_DEX]
16+
)
17+
18+
# Get the user state and print out position information
19+
user_state = info.user_state(address)
20+
positions = []
21+
for position in user_state["assetPositions"]:
22+
positions.append(position["position"])
23+
if len(positions) > 0:
24+
print("positions:")
25+
for position in positions:
26+
print(json.dumps(position, indent=2))
27+
else:
28+
print("no open positions")
29+
30+
# Print the meta for DUMMY_DEX
31+
print("dummy dex meta:", info.meta(dex=DUMMY_DEX))
32+
33+
# Place an order that should rest by setting the price very low
34+
order_result = exchange.order(COIN, True, 20, 1, {"limit": {"tif": "Gtc"}})
35+
print(order_result)
36+
37+
# Query the order status by oid
38+
if order_result["status"] == "ok":
39+
status = order_result["response"]["data"]["statuses"][0]
40+
if "resting" in status:
41+
order_status = info.query_order_by_oid(address, status["resting"]["oid"])
42+
print("Order status by oid:", order_status)
43+
44+
# Cancel the order
45+
if order_result["status"] == "ok":
46+
status = order_result["response"]["data"]["statuses"][0]
47+
if "resting" in status:
48+
cancel_result = exchange.cancel(COIN, status["resting"]["oid"])
49+
print(cancel_result)
50+
51+
52+
if __name__ == "__main__":
53+
main()

examples/basic_recover_user.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from hyperliquid.utils.signing import (
2+
TOKEN_DELEGATE_TYPES,
3+
recover_agent_or_user_from_l1_action,
4+
recover_user_from_user_signed_action,
5+
)
6+
7+
8+
def main():
9+
example_l1_signed_action = {
10+
"signature": {
11+
"r": "0xd088ceb979ab7616f21fd7dabee04342235bd3af6d82a6d153b503c34c73bc93",
12+
"s": "0x425d8467a69f4d0ff6d9ddfb360ef6152c8165cdd20329e03b0a8f19890d73e",
13+
"v": 27,
14+
},
15+
"vaultAddress": "0xc64cc00b46101bd40aa1c3121195e85c0b0918d8",
16+
"action": {"type": "cancel", "cancels": [{"a": 87, "o": 28800768235}]},
17+
"nonce": 1745532560074,
18+
}
19+
agent_or_user = recover_agent_or_user_from_l1_action(
20+
example_l1_signed_action["action"],
21+
example_l1_signed_action["signature"],
22+
example_l1_signed_action["vaultAddress"],
23+
example_l1_signed_action["nonce"],
24+
None,
25+
False,
26+
)
27+
print("recovered l1 action agent or user:", agent_or_user)
28+
29+
example_user_signed_action = {
30+
"signature": {
31+
"r": "0xa00406eb38821b8918743fab856c103132261e8d990852a8ee25e6f2e88891b",
32+
"s": "0x34cf47cfbf09173bcb851bcfdce3ad83dd64ed791ab32bfe9606d25e7c608859",
33+
"v": 27,
34+
},
35+
"action": {
36+
"type": "tokenDelegate",
37+
"signatureChainId": "0xa4b1",
38+
"hyperliquidChain": "Mainnet",
39+
"validator": "0x5ac99df645f3414876c816caa18b2d234024b487",
40+
"wei": 100163871320,
41+
"isUndelegate": True,
42+
"nonce": 1744932112279,
43+
},
44+
"isFrontend": True,
45+
"nonce": 1744932112279,
46+
}
47+
48+
user = recover_user_from_user_signed_action(
49+
example_user_signed_action["action"],
50+
example_user_signed_action["signature"],
51+
TOKEN_DELEGATE_TYPES,
52+
"HyperliquidTransaction:TokenDelegate",
53+
True,
54+
)
55+
print("recovered user-signed action user:", user)
56+
57+
58+
if __name__ == "__main__":
59+
main()

examples/c_signer.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Example script to run CSigner actions
2+
# See https://github.com/hyperliquid-dex/node?tab=readme-ov-file#begin-validating for spec
3+
#
4+
# IMPORTANT: Replace any arguments for the exchange calls below to match your deployment requirements.
5+
6+
import example_utils
7+
8+
from hyperliquid.utils import constants
9+
10+
# Change to one of "Jail" or "Unjail"
11+
ACTION = ""
12+
13+
14+
def main():
15+
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
16+
17+
if ACTION == "Jail":
18+
jail_result = exchange.c_signer_jail_self()
19+
print("jail result", jail_result)
20+
elif ACTION == "Unjail":
21+
unjail_result = exchange.c_signer_unjail_self()
22+
print("unjail result", unjail_result)
23+
else:
24+
raise ValueError("Invalid action specified")
25+
26+
27+
if __name__ == "__main__":
28+
main()

examples/c_validator.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Example script to register a validator
2+
# See https://github.com/hyperliquid-dex/node?tab=readme-ov-file#join-network for spec
3+
#
4+
# IMPORTANT: Replace any arguments for the exchange calls below to match your deployment requirements.
5+
6+
import example_utils
7+
8+
from hyperliquid.utils import constants
9+
10+
# Change to one of "Register", "ChangeProfile", or "Unregister"
11+
ACTION = ""
12+
DUMMY_SIGNER = "0x0000000000000000000000000000000000000001"
13+
14+
15+
def main():
16+
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
17+
18+
if ACTION == "Register":
19+
node_ip = "1.2.3.4"
20+
name = "..."
21+
description = "..."
22+
delegations_disabled = True
23+
commission_bps = 5
24+
signer = DUMMY_SIGNER
25+
unjailed = False
26+
initial_wei = 100000
27+
register_result = exchange.c_validator_register(
28+
node_ip,
29+
name,
30+
description,
31+
delegations_disabled,
32+
commission_bps,
33+
signer,
34+
unjailed,
35+
initial_wei,
36+
)
37+
print("register result", register_result)
38+
elif ACTION == "ChangeProfile":
39+
node_ip = None
40+
name = None
41+
description = None
42+
unjailed = False
43+
disable_delegations = None
44+
commission_bps = None
45+
signer = None
46+
change_profile_result = exchange.c_validator_change_profile(
47+
node_ip,
48+
name,
49+
description,
50+
unjailed,
51+
disable_delegations,
52+
commission_bps,
53+
signer,
54+
)
55+
print("change profile result", change_profile_result)
56+
elif ACTION == "Unregister":
57+
unregister_result = exchange.c_validator_unregister()
58+
print("unregister result", unregister_result)
59+
else:
60+
raise ValueError("Invalid action specified")
61+
62+
63+
if __name__ == "__main__":
64+
main()

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/example_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from hyperliquid.info import Info
99

1010

11-
def setup(base_url=None, skip_ws=False):
11+
def setup(base_url=None, skip_ws=False, perp_dexs=None):
1212
config_path = os.path.join(os.path.dirname(__file__), "config.json")
1313
with open(config_path) as f:
1414
config = json.load(f)
@@ -19,7 +19,7 @@ def setup(base_url=None, skip_ws=False):
1919
print("Running with account address:", address)
2020
if address != account.address:
2121
print("Running with agent address:", account.address)
22-
info = Info(base_url, skip_ws)
22+
info = Info(base_url, skip_ws, perp_dexs=perp_dexs)
2323
user_state = info.user_state(address)
2424
spot_user_state = info.spot_user_state(address)
2525
margin_summary = user_state["marginSummary"]
@@ -28,7 +28,7 @@ def setup(base_url=None, skip_ws=False):
2828
url = info.base_url.split(".", 1)[1]
2929
error_string = f"No accountValue:\nIf you think this is a mistake, make sure that {address} has a balance on {url}.\nIf address shown is your API wallet address, update the config to specify the address of your account, not the address of the API wallet."
3030
raise Exception(error_string)
31-
exchange = Exchange(account, base_url, account_address=address)
31+
exchange = Exchange(account, base_url, account_address=address, perp_dexs=perp_dexs)
3232
return address, info, exchange
3333

3434

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

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import argparse
2+
import json
3+
import os
4+
from collections import defaultdict
5+
6+
import lz4.frame
7+
8+
from hyperliquid.utils.signing import TOKEN_DELEGATE_TYPES, recover_user_from_user_signed_action
9+
10+
REPLICA_CMD_BATCH_SIZE = 10000
11+
12+
13+
def decompress_lz4(input_file, output_file):
14+
with open(input_file, "rb") as f_in:
15+
compressed_data = f_in.read()
16+
17+
decompressed_data = lz4.frame.decompress(compressed_data)
18+
19+
with open(output_file, "wb") as f_out:
20+
f_out.write(decompressed_data)
21+
22+
23+
def main():
24+
parser = argparse.ArgumentParser(description="parse token delegate actions from replica cmds")
25+
parser.add_argument("--data-dir", type=str, required=True)
26+
parser.add_argument("--start-height", type=int, required=True)
27+
parser.add_argument("--end-height", type=int, required=True)
28+
args = parser.parse_args()
29+
30+
data_dir = args.data_dir
31+
start_height = args.start_height
32+
end_height = args.end_height
33+
34+
if start_height % REPLICA_CMD_BATCH_SIZE == 0:
35+
raise Exception("start height is not aligned with replica cmd batch size")
36+
if end_height % REPLICA_CMD_BATCH_SIZE == 0:
37+
raise Exception("end height is not aligned with replica cmd batch size")
38+
39+
flns = []
40+
for height in range(start_height, end_height, REPLICA_CMD_BATCH_SIZE):
41+
lz4_fln = f"{data_dir}/{height}.lz4"
42+
if not os.path.exists(lz4_fln):
43+
raise Exception(
44+
f"replica cmds file at {height} not found - download missing block files(s) using 'aws s3 cp s3://hl-[testnet | mainnet]-replica-cmds/<block_object_path> --request-payer requester'"
45+
)
46+
fln = f"{data_dir}/{height}"
47+
decompress_lz4(lz4_fln, fln)
48+
flns.append(fln)
49+
50+
user_to_validator_to_amount: dict[str, dict[str, int]] = defaultdict(lambda: defaultdict(int))
51+
52+
for fln in flns:
53+
f = open(fln)
54+
lines = f.readlines()
55+
for line in lines:
56+
if "tokenDelegate" not in line:
57+
continue
58+
data = json.loads(line)
59+
bundles = data["abci_block"]["signed_action_bundles"]
60+
for bundle in bundles:
61+
for signed_action in bundle[1]["signed_actions"]:
62+
action = signed_action["action"]
63+
if action["type"] != "tokenDelegate":
64+
continue
65+
validator = action["validator"]
66+
wei = action["wei"]
67+
is_delegate = not action["isUndelegate"]
68+
user = recover_user_from_user_signed_action(
69+
action,
70+
signed_action["signature"],
71+
TOKEN_DELEGATE_TYPES,
72+
"HyperliquidTransaction:TokenDelegate",
73+
True,
74+
)
75+
if not is_delegate:
76+
wei = -wei
77+
user_to_validator_to_amount[user][validator] += wei / 100_000_000 # native token wei decimals
78+
79+
print("user to validator to wei amount delegated", user_to_validator_to_amount)
80+
81+
82+
if __name__ == "__main__":
83+
main()

0 commit comments

Comments
 (0)