Skip to content

Commit

Permalink
Version 0.8.0
Browse files Browse the repository at this point in the history
  • Loading branch information
traderben committed Oct 24, 2024
1 parent c69fa7a commit 299cf2b
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 3 deletions.
4 changes: 2 additions & 2 deletions assets/images/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions examples/basic_convert_to_multi_sig_signer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from hyperliquid.utils import constants
import example_utils


def main():
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)

if exchange.account_address != exchange.wallet.address:
raise Exception("Agents do not have permission to convert to multi-sig signer")

# the user owning this signer (either the user itself or agent's user) should already be registered as authorized user for the multi-sig user
signer = "0x0000000000000000000000000000000000000000"
multi_sig_user = "0x0000000000000000000000000000000000000001"
convert_result = exchange.convert_to_multi_sig_signer(signer, multi_sig_user)
print(convert_result)


if __name__ == "__main__":
main()
21 changes: 21 additions & 0 deletions examples/basic_convert_to_multi_sig_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from hyperliquid.utils import constants
import example_utils


def main():
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)

if exchange.account_address != exchange.wallet.address:
raise Exception("Agents do not have permission to convert to multi-sig user")

# authorized users are the users for which one can themselves or agents use as signers
# for multi-sig actions
authorized_user_1 = "0x0000000000000000000000000000000000000000"
authorized_user_2 = "0x0000000000000000000000000000000000000001"
threshold = 1
convert_result = exchange.convert_to_multi_sig_user([authorized_user_1, authorized_user_2], threshold)
print(convert_result)


if __name__ == "__main__":
main()
32 changes: 32 additions & 0 deletions examples/basic_multi_sig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from hyperliquid.utils import constants
from hyperliquid.utils.signing import sign_usd_transfer_action, get_timestamp_ms
from hyperliquid.utils.types import Any, List
import example_utils


def main():
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
multi_sig_wallets = example_utils.setup_multi_sig_wallets()

multi_sig_user = "0x0000000000000000000000000000000000000005"

timestamp = get_timestamp_ms()
action = {
"type": "usdSend",
"signatureChainId": "0x66eee",
"hyperliquidChain": "Testnet",
"destination": "0x0000000000000000000000000000000000000000",
"amount": "100.0",
"time": timestamp,
}
signatures: List[Any] = []
for wallet in multi_sig_wallets:
signature = sign_usd_transfer_action(wallet, action, False)
signatures.append(signature)

multi_sig_result = exchange.multi_sig(multi_sig_user, action, signatures, timestamp)
print(multi_sig_result)


if __name__ == "__main__":
main()
15 changes: 15 additions & 0 deletions examples/example_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,18 @@ def setup(base_url=None, skip_ws=False):
raise Exception(error_string)
exchange = Exchange(account, base_url, account_address=address)
return address, info, exchange


def setup_multi_sig_wallets():
config_path = os.path.join(os.path.dirname(__file__), "multi_sig_wallets.json")
with open(config_path) as f:
config = json.load(f)
wallets = []
for wallet_config in config:
account: LocalAccount = eth_account.Account.from_key(wallet_config["secret_key"])
address = wallet_config["account_address"]
if account.address != address:
raise Exception(f"provided signer address {address} does not match private key")
print("loaded multi-sig signer", address)
wallets.append(account)
return wallets
12 changes: 12 additions & 0 deletions examples/multi_sig_wallets.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"comment": "signer 1",
"secret_key": "",
"account_address": ""
},
{
"comment": "signer 2",
"secret_key": "",
"account_address": ""
}
]
58 changes: 58 additions & 0 deletions hyperliquid/exchange.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import logging
import secrets

Expand Down Expand Up @@ -27,6 +28,8 @@
sign_usd_class_transfer_action,
sign_usd_transfer_action,
sign_withdraw_from_bridge_action,
sign_convert_to_multi_sig_user_action,
sign_convert_to_multi_sig_signer_action,
)
from hyperliquid.utils.types import Any, BuilderInfo, Cloid, List, Meta, Optional, SpotMeta, Tuple

Expand Down Expand Up @@ -549,3 +552,58 @@ def approve_builder_fee(self, builder: str, max_fee_rate: str) -> Any:
action = {"maxFeeRate": max_fee_rate, "builder": builder, "nonce": timestamp, "type": "approveBuilderFee"}
signature = sign_approve_builder_fee(self.wallet, action, self.base_url == MAINNET_API_URL)
return self._post_action(action, signature, timestamp)

def convert_to_multi_sig_user(self, authorized_users: List[str], threshold: int) -> Any:
timestamp = get_timestamp_ms()
authorized_users = sorted(authorized_users)
signers = {
"authorizedUsers": authorized_users,
"threshold": threshold,
}
action = {
"type": "convertToMultiSigUser",
"signers": json.dumps(signers),
"nonce": timestamp,
}
signature = sign_convert_to_multi_sig_user_action(self.wallet, action, self.base_url == MAINNET_API_URL)
return self._post_action(
action,
signature,
timestamp,
)

def convert_to_multi_sig_signer(self, signer: str, multi_sig_user: str) -> Any:
timestamp = get_timestamp_ms()
action = {
"type": "convertToMultiSigUser",
"signer": signer,
"multi_sig_user": multi_sig_user,
"nonce": timestamp,
}
signature = sign_convert_to_multi_sig_signer_action(self.wallet, action, self.base_url == MAINNET_API_URL)
return self._post_action(
action,
signature,
timestamp,
)

def multi_sig(self, multi_sig_user, inner_action, signatures, nonce, vault_address=None):
multi_sig_action = {
"type": "multiSig",
"user": multi_sig_user.lower(),
"signatures": signatures,
"inner": inner_action,
}
signature = sign_l1_action(
self.wallet,
multi_sig_action,
vault_address,
nonce,
self.base_url == MAINNET_API_URL,
)

return self._post_action(
multi_sig_action,
signature,
nonce,
)
6 changes: 6 additions & 0 deletions hyperliquid/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,12 @@ def query_referral_state(self, user: str) -> Any:
def query_sub_accounts(self, user: str) -> Any:
return self.post("/info", {"type": "subAccounts", "user": user})

def query_user_to_multi_sig_signers(self, multi_sig_user: str) -> Any:
return self.post("/info", {"type": "userToMultiSigSigners", "user": multi_sig_user})

def query_signer_to_multi_sig_user(self, signer: str) -> Any:
return self.post("/info", {"type": "signerToMultiSigUser", "signer": signer})

def subscribe(self, subscription: Subscription, callback: Callable[[Any], None]) -> int:
if subscription["type"] == "l2Book" or subscription["type"] == "trades" or subscription["type"] == "candle":
subscription["coin"] = self.name_to_coin[subscription["coin"]]
Expand Down
29 changes: 29 additions & 0 deletions hyperliquid/utils/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,35 @@ def sign_usd_class_transfer_action(wallet, action, is_mainnet):
)


def sign_convert_to_multi_sig_user_action(wallet, action, is_mainnet):
return sign_user_signed_action(
wallet,
action,
[
{"name": "hyperliquidChain", "type": "string"},
{"name": "signers", "type": "string"},
{"name": "nonce", "type": "uint64"},
],
"HyperliquidTransaction:ConvertToMultiSigUser",
is_mainnet,
)


def sign_convert_to_multi_sig_signer_action(wallet, action, is_mainnet):
return sign_user_signed_action(
wallet,
action,
[
{"name": "hyperliquidChain", "type": "string"},
{"name": "signer", "type": "address"},
{"name": "multiSigUser", "type": "address"},
{"name": "nonce", "type": "uint64"},
],
"HyperliquidTransaction:ConvertToMultiSigSigner",
is_mainnet,
)


def sign_agent(wallet, action, is_mainnet):
return sign_user_signed_action(
wallet,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "hyperliquid-python-sdk"
version = "0.7.1"
version = "0.8.0"
description = "SDK for Hyperliquid API trading with Python."
readme = "README.md"
authors = ["Hyperliquid <[email protected]>"]
Expand Down

0 comments on commit 299cf2b

Please sign in to comment.