Skip to content

Commit 239e5f1

Browse files
committed
Version 0.9.0
1 parent a810da0 commit 239e5f1

File tree

10 files changed

+181
-87
lines changed

10 files changed

+181
-87
lines changed

examples/basic_builder_fee.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ def main():
1010
raise Exception("Only the main wallet has permission to approve a builder fee")
1111

1212
# approve setting a builder fee
13-
approve_result = exchange.approve_builder_fee("0x0000000000000000000000000000000000000000", "0.001%")
13+
approve_result = exchange.approve_builder_fee("0x8c967E73E7B15087c42A10D344cFf4c96D877f1D", "0.001%")
1414
print(approve_result)
1515

1616
# place an order with builder set, this will cause an additional fee to be added to the order which is sent to the builder
1717
order_result = exchange.market_open(
18-
"ETH", True, 0.05, None, 0.01, builder={"b": "0x0000000000000000000000000000000000000000", "f": 1}
18+
"ETH", True, 0.05, None, 0.01, builder={"b": "0x8c967E73E7B15087c42A10D344cFf4c96D877f1D", "f": 1}
1919
)
2020
print(order_result)
2121

examples/basic_convert_to_multi_sig_user.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ def main():
88
if exchange.account_address != exchange.wallet.address:
99
raise Exception("Agents do not have permission to convert to multi-sig user")
1010

11-
# authorized users are the users for which one can themselves or agents use as signers
12-
# for multi-sig actions
11+
# Authorized users are the public addresses of the wallets that will be able to sign on behalf of the multi-sig user.
12+
# Some additional notes:
13+
# Only existing users may be used. In other words, the authorized users must have deposited. Otherwise this conversion will fail.
14+
# The multi-sig signatures must be generated by the authorized user's wallet. Agent/API wallets cannot be used.
1315
authorized_user_1 = "0x0000000000000000000000000000000000000000"
1416
authorized_user_2 = "0x0000000000000000000000000000000000000001"
1517
threshold = 1

examples/basic_multi_sig.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from hyperliquid.utils import constants
2-
from hyperliquid.utils.signing import sign_usd_transfer_action, get_timestamp_ms
2+
from hyperliquid.utils.signing import get_timestamp_ms, sign_multi_sig_inner, USD_SEND_SIGN_TYPES
33
from hyperliquid.utils.types import Any, List
44
import example_utils
55

@@ -8,9 +8,16 @@ def main():
88
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
99
multi_sig_wallets = example_utils.setup_multi_sig_wallets()
1010

11+
# The outer signer is required to be an authorized user or an agent of the authorized user of the multi-sig user.
12+
13+
# Address of the multi-sig user that the action will be executed for
14+
# Executing the action requires at least the specified threshold of signatures
15+
# required for that multi-sig user
1116
multi_sig_user = "0x0000000000000000000000000000000000000005"
1217

1318
timestamp = get_timestamp_ms()
19+
20+
# Define the multi-sig inner action - in this case, sending USD
1421
action = {
1522
"type": "usdSend",
1623
"signatureChainId": "0x66eee",
@@ -20,10 +27,23 @@ def main():
2027
"time": timestamp,
2128
}
2229
signatures: List[Any] = []
30+
31+
# Collect signatures from each wallet in multi_sig_wallets. Each wallet must belong to a user.
2332
for wallet in multi_sig_wallets:
24-
signature = sign_usd_transfer_action(wallet, action, False)
33+
# Sign the action with each wallet
34+
signature = sign_multi_sig_inner(
35+
wallet,
36+
action,
37+
exchange.base_url == constants.MAINNET_API_URL,
38+
USD_SEND_SIGN_TYPES,
39+
"HyperliquidTransaction:UsdSend",
40+
multi_sig_user,
41+
address,
42+
)
2543
signatures.append(signature)
2644

45+
# Execute the multi-sig action with all collected signatures
46+
# This will only succeed if enough valid signatures are provided
2747
multi_sig_result = exchange.multi_sig(multi_sig_user, action, signatures, timestamp)
2848
print(multi_sig_result)
2949

examples/config.json.example

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,24 @@
55
If you are using an Agent/API Wallet you MUST also specify the public address of your account, not the
66
address of the Agent/API Wallet.
77
Otherwise, feel free to leave it blank and it will be automatically derived from the secret key.
8+
9+
You can also populate the "multi_sig" section with the secret keys of the authorized user wallets that you
10+
wish to sign multi-sig actions for.
811
",
912
"secret_key": "",
10-
"account_address": ""
11-
}
13+
"account_address": "",
14+
"multi_sig": {
15+
"authorized_users": [
16+
{
17+
"comment": "signer 1",
18+
"secret_key": "",
19+
"account_address": ""
20+
},
21+
{
22+
"comment": "signer 2",
23+
"secret_key": "",
24+
"account_address": ""
25+
}
26+
]
27+
}
28+
}

examples/example_utils.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,16 @@ def setup(base_url=None, skip_ws=False):
3333

3434

3535
def setup_multi_sig_wallets():
36-
config_path = os.path.join(os.path.dirname(__file__), "multi_sig_wallets.json")
36+
config_path = os.path.join(os.path.dirname(__file__), "config.json")
3737
with open(config_path) as f:
3838
config = json.load(f)
39-
wallets = []
40-
for wallet_config in config:
39+
40+
authorized_user_wallets = []
41+
for wallet_config in config["multi_sig"]["authorized_users"]:
4142
account: LocalAccount = eth_account.Account.from_key(wallet_config["secret_key"])
4243
address = wallet_config["account_address"]
4344
if account.address != address:
44-
raise Exception(f"provided signer address {address} does not match private key")
45-
print("loaded multi-sig signer", address)
46-
wallets.append(account)
47-
return wallets
45+
raise Exception(f"provided authorized user address {address} does not match private key")
46+
print("loaded authorized user for multi-sig", address)
47+
authorized_user_wallets.append(account)
48+
return authorized_user_wallets

hyperliquid/exchange.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
sign_usd_transfer_action,
3030
sign_withdraw_from_bridge_action,
3131
sign_convert_to_multi_sig_user_action,
32-
sign_convert_to_multi_sig_signer_action,
32+
sign_multi_sig_action,
3333
)
3434
from hyperliquid.utils.types import Any, BuilderInfo, Cloid, List, Meta, Optional, SpotMeta, Tuple
3535

@@ -112,6 +112,8 @@ def bulk_orders(self, order_requests: List[OrderRequest], builder: Optional[Buil
112112
]
113113
timestamp = get_timestamp_ms()
114114

115+
if builder:
116+
builder["b"] = builder["b"].lower()
115117
order_action = order_wires_to_order_action(order_wires, builder)
116118

117119
signature = sign_l1_action(
@@ -572,36 +574,26 @@ def convert_to_multi_sig_user(self, authorized_users: List[str], threshold: int)
572574
timestamp,
573575
)
574576

575-
def convert_to_multi_sig_signer(self, signer: str, multi_sig_user: str) -> Any:
576-
timestamp = get_timestamp_ms()
577-
action = {
578-
"type": "convertToMultiSigUser",
579-
"signer": signer,
580-
"multi_sig_user": multi_sig_user,
581-
"nonce": timestamp,
582-
}
583-
signature = sign_convert_to_multi_sig_signer_action(self.wallet, action, self.base_url == MAINNET_API_URL)
584-
return self._post_action(
585-
action,
586-
signature,
587-
timestamp,
588-
)
589-
590577
def multi_sig(self, multi_sig_user, inner_action, signatures, nonce, vault_address=None):
578+
multi_sig_user = multi_sig_user.lower()
591579
multi_sig_action = {
592580
"type": "multiSig",
593-
"user": multi_sig_user.lower(),
581+
"signatureChainId": "0x66eee",
594582
"signatures": signatures,
595-
"inner": inner_action,
583+
"payload": {
584+
"multiSigUser": multi_sig_user,
585+
"outerSigner": self.wallet.address.lower(),
586+
"action": inner_action,
587+
},
596588
}
597-
signature = sign_l1_action(
589+
is_mainnet = self.base_url == MAINNET_API_URL
590+
signature = sign_multi_sig_action(
598591
self.wallet,
599592
multi_sig_action,
593+
is_mainnet,
600594
vault_address,
601595
nonce,
602-
self.base_url == MAINNET_API_URL,
603596
)
604-
605597
return self._post_action(
606598
multi_sig_action,
607599
signature,

hyperliquid/info.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,9 @@ def user_fills_by_time(self, address: str, start_time: int, end_time: Optional[i
216216
...
217217
]
218218
"""
219-
return self.post("/info", {"type": "userFillsByTime", "user": address, "startTime": start_time, "endTime": end_time})
219+
return self.post(
220+
"/info", {"type": "userFillsByTime", "user": address, "startTime": start_time, "endTime": end_time}
221+
)
220222

221223
def meta(self) -> Meta:
222224
"""Retrieve exchange perp metadata
@@ -506,9 +508,6 @@ def query_sub_accounts(self, user: str) -> Any:
506508
def query_user_to_multi_sig_signers(self, multi_sig_user: str) -> Any:
507509
return self.post("/info", {"type": "userToMultiSigSigners", "user": multi_sig_user})
508510

509-
def query_signer_to_multi_sig_user(self, signer: str) -> Any:
510-
return self.post("/info", {"type": "signerToMultiSigUser", "signer": signer})
511-
512511
def subscribe(self, subscription: Subscription, callback: Callable[[Any], None]) -> int:
513512
if subscription["type"] == "l2Book" or subscription["type"] == "trades" or subscription["type"] == "candle":
514513
subscription["coin"] = self.name_to_coin[subscription["coin"]]

0 commit comments

Comments
 (0)